55
66import com .azure .core .exception .UnexpectedLengthException ;
77import com .azure .core .util .CoreUtils ;
8+ import com .azure .core .util .FluxUtil ;
89import com .azure .core .util .UrlBuilder ;
910import com .azure .core .util .logging .ClientLogger ;
1011import com .azure .storage .common .implementation .StorageImplUtils ;
1112import reactor .core .publisher .Flux ;
12- import reactor .core .publisher .Mono ;
1313
1414import java .io .IOException ;
1515import java .io .InputStream ;
16+ import java .io .UncheckedIOException ;
1617import java .io .UnsupportedEncodingException ;
1718import java .net .URLDecoder ;
1819import java .net .URLEncoder ;
@@ -188,7 +189,7 @@ public static Flux<ByteBuffer> convertStreamToByteBuffer(InputStream data, long
188189 /**
189190 * A utility method for converting the input stream to Flux of ByteBuffer. Will check the equality of entity length
190191 * and the input length.
191- *
192+ * <p>
192193 * Using markAndReset=true to force a seekable stream implies a buffering strategy is not being used, in which case
193194 * length is still needed for whatever underlying REST call is being streamed to. If markAndReset=false and data is
194195 * being buffered, consider using {@link com.azure.core.util.FluxUtil#toFluxByteBuffer(InputStream, int)} which
@@ -205,78 +206,97 @@ public static Flux<ByteBuffer> convertStreamToByteBuffer(InputStream data, long
205206 * @throws RuntimeException When I/O error occurs.
206207 */
207208 public static Flux <ByteBuffer > convertStreamToByteBuffer (InputStream data , long length , int blockSize ,
208- boolean markAndReset ) {
209+ boolean markAndReset ) {
209210 if (markAndReset ) {
210211 data .mark (Integer .MAX_VALUE );
211212 }
213+
212214 if (length == 0 ) {
213215 try {
214216 if (data .read () != -1 ) {
215217 long totalLength = 1 + data .available ();
216- throw LOGGER . logExceptionAsError ( new UnexpectedLengthException (
217- String . format ( "Request body emitted %d bytes, more than the expected %d bytes." ,
218- totalLength , length ), totalLength , length ));
218+ return FluxUtil . fluxError ( LOGGER , new UnexpectedLengthException ( String . format (
219+ "Request body emitted %d bytes, more than the expected %d bytes." , totalLength , length ) ,
220+ totalLength , length ));
219221 }
220222 } catch (IOException e ) {
221- throw LOGGER . logExceptionAsError ( new RuntimeException ( "I/O errors occurred" , e ));
223+ return FluxUtil . fluxError ( LOGGER , new UncheckedIOException ( e ));
222224 }
223225 }
226+
224227 return Flux .defer (() -> {
225228 /*
226- If the request needs to be retried, the flux will be resubscribed to. The stream and counter must be
227- reset in order to correctly return the same data again.
229+ * If the request needs to be retried, the flux will be resubscribed to. The stream and counter must be
230+ * reset in order to correctly return the same data again.
228231 */
229- final long [] currentTotalLength = new long [1 ];
230232 if (markAndReset ) {
231233 try {
232234 data .reset ();
233235 } catch (IOException e ) {
234- throw LOGGER . logExceptionAsError ( new RuntimeException (e ));
236+ return FluxUtil . fluxError ( LOGGER , new UncheckedIOException (e ));
235237 }
236238 }
237- return Flux .range (0 , (int ) Math .ceil ((double ) length / (double ) blockSize ))
238- .map (i -> i * blockSize )
239- .concatMap (pos -> Mono .fromCallable (() -> {
240- long count = pos + blockSize > length ? length - pos : blockSize ;
241- byte [] cache = new byte [(int ) count ];
242- int numOfBytes = 0 ;
243- int offset = 0 ;
244- // Revise the casting if the max allowed network data transmission is over 2G.
245- int len = (int ) count ;
246- while (numOfBytes != -1 && offset < count ) {
239+
240+ final long [] currentTotalLength = new long [1 ];
241+ return Flux .generate (() -> data , (is , sink ) -> {
242+ long pos = currentTotalLength [0 ];
243+
244+ long count = (pos + blockSize ) > length ? (length - pos ) : blockSize ;
245+ byte [] cache = new byte [(int ) count ];
246+
247+ int numOfBytes = 0 ;
248+ int offset = 0 ;
249+ // Revise the casting if the max allowed network data transmission is over 2G.
250+ int len = (int ) count ;
251+
252+ while (numOfBytes != -1 && offset < count ) {
253+ try {
247254 numOfBytes = data .read (cache , offset , len );
248255 if (numOfBytes != -1 ) {
249256 offset += numOfBytes ;
250257 len -= numOfBytes ;
251258 currentTotalLength [0 ] += numOfBytes ;
252259 }
260+ } catch (IOException e ) {
261+ sink .error (e );
262+ return is ;
253263 }
254- if (numOfBytes == -1 && currentTotalLength [0 ] < length ) {
255- throw LOGGER .logExceptionAsError (new UnexpectedLengthException (
256- String .format ("Request body emitted %d bytes, less than the expected %d bytes." ,
257- currentTotalLength [0 ], length ), currentTotalLength [0 ], length ));
258- }
264+ }
259265
260- // Validate that stream isn't longer.
261- if (currentTotalLength [0 ] >= length ) {
262- try {
263- if (data .read () != -1 ) {
264- long totalLength = 1 + currentTotalLength [0 ] + data .available ();
265- throw LOGGER .logExceptionAsError (new UnexpectedLengthException (
266- String .format ("Request body emitted %d bytes, more than the expected %d bytes." ,
267- totalLength , length ), totalLength , length ));
268- } else if (currentTotalLength [0 ] > length ) {
269- throw LOGGER .logExceptionAsError (new IllegalStateException (
270- String .format ("Read more data than was requested. Size of data read: %d. Size of data"
271- + " requested: %d" , currentTotalLength [0 ], length )));
272- }
273- } catch (IOException e ) {
274- throw LOGGER .logExceptionAsError (new RuntimeException ("I/O errors occurred" , e ));
266+ if (numOfBytes == -1 && currentTotalLength [0 ] < length ) {
267+ sink .error (LOGGER .logExceptionAsError (new UnexpectedLengthException (String .format (
268+ "Request body emitted %d bytes, less than the expected %d bytes." ,
269+ currentTotalLength [0 ], length ), currentTotalLength [0 ], length )));
270+ return is ;
271+ }
272+
273+ // Validate that stream isn't longer.
274+ if (currentTotalLength [0 ] >= length ) {
275+ try {
276+ if (data .read () != -1 ) {
277+ long totalLength = 1 + currentTotalLength [0 ] + data .available ();
278+ sink .error (LOGGER .logExceptionAsError (new UnexpectedLengthException (
279+ String .format ("Request body emitted %d bytes, more than the expected %d bytes." ,
280+ totalLength , length ), totalLength , length )));
281+ return is ;
282+ } else if (currentTotalLength [0 ] > length ) {
283+ sink .error (LOGGER .logExceptionAsError (new IllegalStateException (
284+ String .format ("Read more data than was requested. Size of data read: %d. Size of data"
285+ + " requested: %d" , currentTotalLength [0 ], length ))));
286+ return is ;
275287 }
288+ } catch (IOException e ) {
289+ sink .error (LOGGER .logExceptionAsError (new RuntimeException ("I/O errors occurred" , e )));
290+ return is ;
276291 }
292+ }
277293
278- return ByteBuffer .wrap (cache , 0 , offset );
279- }));
294+ sink .next (ByteBuffer .wrap (cache , 0 , offset ));
295+ if (currentTotalLength [0 ] == length ) {
296+ sink .complete ();
297+ }
298+ return is ;
299+ });
280300 });
281301 }
282302
0 commit comments