private void notifyListeners(TransferEvent e) { if (transferListeners == null || transferListeners.isEmpty()) return; for (TransferListener l : transferListeners) { switch (e.getType()) { case TransferEvent.EXPORT_DONE: l.exportDone(e); break; } } }
/** * Reads up to {@code length} bytes of data and stores them into {@code buffer}, starting at index * {@code offset}. * * <p>This method blocks until at least one byte of data can be read, the end of the opened range * is detected, or an exception is thrown. * * @param buffer The buffer into which the read data should be stored. * @param offset The start offset into {@code buffer} at which data should be written. * @param readLength The maximum number of bytes to read. * @return The number of bytes read, or {@link C#RESULT_END_OF_INPUT} if the end of the opened * range is reached. * @throws IOException If an error occurs reading from the source. */ private int readInternal(byte[] buffer, int offset, int readLength) throws IOException { readLength = bytesToRead == C.LENGTH_UNBOUNDED ? readLength : (int) Math.min(readLength, bytesToRead - bytesRead); if (readLength == 0) { // We've read all of the requested data. return C.RESULT_END_OF_INPUT; } int read = inputStream.read(buffer, offset, readLength); if (read == -1) { if (bytesToRead != C.LENGTH_UNBOUNDED && bytesToRead != bytesRead) { // The server closed the connection having not sent sufficient data. throw new EOFException(); } return C.RESULT_END_OF_INPUT; } bytesRead += read; if (listener != null) { listener.onBytesTransferred(read); } return read; }
/** * Skips any bytes that need skipping. Else does nothing. * * <p>This implementation is based roughly on {@code libcore.io.Streams.skipByReading()}. * * @throws InterruptedIOException If the thread is interrupted during the operation. * @throws EOFException If the end of the input stream is reached before the bytes are skipped. */ private void skipInternal() throws IOException { if (bytesSkipped == bytesToSkip) { return; } // Acquire the shared skip buffer. byte[] skipBuffer = skipBufferReference.getAndSet(null); if (skipBuffer == null) { skipBuffer = new byte[4096]; } while (bytesSkipped != bytesToSkip) { int readLength = (int) Math.min(bytesToSkip - bytesSkipped, skipBuffer.length); int read = inputStream.read(skipBuffer, 0, readLength); if (Thread.interrupted()) { throw new InterruptedIOException(); } if (read == -1) { throw new EOFException(); } bytesSkipped += read; if (listener != null) { listener.onBytesTransferred(read); } } // Release the shared skip buffer. skipBufferReference.set(skipBuffer); }
@Override public void close() throws HttpDataSourceException { try { if (inputStream != null) { Util.maybeTerminateInputStream(connection, bytesRemaining()); try { inputStream.close(); } catch (IOException e) { throw new HttpDataSourceException(e, dataSpec, HttpDataSourceException.TYPE_CLOSE); } } } finally { inputStream = null; closeConnectionQuietly(); if (opened) { opened = false; if (listener != null) { listener.onTransferEnd(); } } } }
@Override public long open(DataSpec dataSpec) throws HttpDataSourceException { this.dataSpec = dataSpec; this.bytesRead = 0; this.bytesSkipped = 0; try { connection = makeConnection(dataSpec); } catch (IOException e) { throw new HttpDataSourceException( "Unable to connect to " + dataSpec.uri.toString(), e, dataSpec, HttpDataSourceException.TYPE_OPEN); } int responseCode; try { responseCode = connection.getResponseCode(); } catch (IOException e) { closeConnectionQuietly(); throw new HttpDataSourceException( "Unable to connect to " + dataSpec.uri.toString(), e, dataSpec, HttpDataSourceException.TYPE_OPEN); } // Check for a valid response code. if (responseCode < 200 || responseCode > 299) { Map<String, List<String>> headers = connection.getHeaderFields(); closeConnectionQuietly(); throw new InvalidResponseCodeException(responseCode, headers, dataSpec); } // Check for a valid content type. String contentType = connection.getContentType(); if (contentTypePredicate != null && !contentTypePredicate.evaluate(contentType)) { closeConnectionQuietly(); throw new InvalidContentTypeException(contentType, dataSpec); } // If we requested a range starting from a non-zero position and received a 200 rather than a // 206, then the server does not support partial requests. We'll need to manually skip to the // requested position. bytesToSkip = responseCode == 200 && dataSpec.position != 0 ? dataSpec.position : 0; // Determine the length of the data to be read, after skipping. if ((dataSpec.flags & DataSpec.FLAG_ALLOW_GZIP) == 0) { long contentLength = getContentLength(connection); bytesToRead = dataSpec.length != C.LENGTH_UNBOUNDED ? dataSpec.length : contentLength != C.LENGTH_UNBOUNDED ? contentLength - bytesToSkip : C.LENGTH_UNBOUNDED; } else { // Gzip is enabled. If the server opts to use gzip then the content length in the response // will be that of the compressed data, which isn't what we want. Furthermore, there isn't a // reliable way to determine whether the gzip was used or not. Always use the dataSpec length // in this case. bytesToRead = dataSpec.length; } try { inputStream = connection.getInputStream(); } catch (IOException e) { closeConnectionQuietly(); throw new HttpDataSourceException(e, dataSpec, HttpDataSourceException.TYPE_OPEN); } opened = true; if (listener != null) { listener.onTransferStart(); } return bytesToRead; }