使用 BlockingQueue 下载 PDF 文件
PDF file download using BlockingQueue
我正在尝试使用 URLConnection 下载 pdf 文件。这是我设置连接对象的方法。
URL serverUrl = new URL(url);
urlConnection = (HttpURLConnection) serverUrl.openConnection();
urlConnection.setDoInput(true);
urlConnection.setRequestMethod("GET");
urlConnection.setRequestProperty("Content-Type", "application/pdf");
urlConnection.setRequestProperty("ENCTYPE", "multipart/form-data");
String contentLength = urlConnection.getHeaderField("Content-Length");
我从连接对象中获取了输入流。
bufferedInputStream = new BufferedInputStream(urlConnection.getInputStream());
和写入文件内容的输出流。
File dir = new File(context.getFilesDir(), mFolder);
if(!dir.exists()) dir.mkdir();
final File f = new File(dir, String.valueOf(documentName));
f.createNewFile();
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(f, true)); //true for appendMode
创建 BlockingQueue 以便执行读写操作的线程可以访问队列。
final BlockingQueue<ByteArrayWrapper> blockingQueue = new ArrayBlockingQueue<ByteArrayWrapper>(MAX_VALUE,true);
final byte[] dataBuffer = new byte[MAX_VALUE];
现在已创建线程以从 InputStream 读取数据。
Thread readerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
int count = 0;
while((count = bufferedInputStream.read(dataBuffer, 0, dataBuffer.length)) != -1) {
ByteArrayWrapper byteArrayWrapper = new ByteArrayWrapper(dataBuffer);
byteArrayWrapper.setBytesReadCount(count);
blockingQueue.put(byteArrayWrapper);
}
blockingQueue.put(null); //end of file
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
现在编写器线程读取这些文件内容。
Thread writerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
while(true) {
ByteArrayWrapper byteWrapper = blockingQueue.take();
if(null == byteWrapper) break;
bufferedOutputStream.write(byteWrapper.getBytesRead(), 0, byteWrapper.getBytesReadCount());
}
bufferedOutputStream.flush();
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
线程终于启动了。
readerThread.start();
writerThread.start();
理论上应该从InputStream中读取文件并保存到目标文件中。但是,实际上,它会生成空白的 pdf 文件。其他时候,它显示无效的pdf格式异常。文件大小与 InputStream 的内容长度匹配。有什么我想念的吗?
我不熟悉ByteArrayWrapper
。它是否只包含对数组的引用,就像这样?
public class ByteArrayBuffer {
final private byte[] data;
public ByteArrayBuffer(byte[] data) {
this.data = data;
}
public byte[] getBytesRead() {
return data;
}
/*...etc...*/
}
如果是的话。这就是问题所在:所有 ByteArrayWrapper
对象都由同一个数组支持。被作者反复覆盖。尽管 BlockingQueue
努力将每个对象从一个线程安全地发布到另一个线程。
最简单的修复方法可能是使 ByteArrayWrapper
有效不可变,即在将其发布到另一个线程后不要更改它。在构造时复制数组是最简单的:
public ByteArrayWrapper(byte[] data) {
this.data = Arrays.copyOf(data, data.length);
}
另一个问题是 "BlockingQueue does not accept null elements"(参见 BlockingQueue docs),因此 "end of input" 标记值不起作用。将 null
替换为
private static ByteArrayWrapper END = new ByteArrayWrapper(new byte[]{});
在适当的地方会解决这个问题。
通过对代码副本进行这些更改,我能够检索到 PDF 文件的忠实副本。
尽量使用Android DownloadManager
(http://developer.android.com/reference/android/app/DownloadManager.html)它用来在后台处理long-运行 HTTP请求。
这里不需要考虑收到的字节数,进度会显示在通知栏中。
这里有一个很好的教程:http://blog.vogella.com/2011/06/14/android-downloadmanager-example/
我正在尝试使用 URLConnection 下载 pdf 文件。这是我设置连接对象的方法。
URL serverUrl = new URL(url);
urlConnection = (HttpURLConnection) serverUrl.openConnection();
urlConnection.setDoInput(true);
urlConnection.setRequestMethod("GET");
urlConnection.setRequestProperty("Content-Type", "application/pdf");
urlConnection.setRequestProperty("ENCTYPE", "multipart/form-data");
String contentLength = urlConnection.getHeaderField("Content-Length");
我从连接对象中获取了输入流。
bufferedInputStream = new BufferedInputStream(urlConnection.getInputStream());
和写入文件内容的输出流。
File dir = new File(context.getFilesDir(), mFolder);
if(!dir.exists()) dir.mkdir();
final File f = new File(dir, String.valueOf(documentName));
f.createNewFile();
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(f, true)); //true for appendMode
创建 BlockingQueue 以便执行读写操作的线程可以访问队列。
final BlockingQueue<ByteArrayWrapper> blockingQueue = new ArrayBlockingQueue<ByteArrayWrapper>(MAX_VALUE,true);
final byte[] dataBuffer = new byte[MAX_VALUE];
现在已创建线程以从 InputStream 读取数据。
Thread readerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
int count = 0;
while((count = bufferedInputStream.read(dataBuffer, 0, dataBuffer.length)) != -1) {
ByteArrayWrapper byteArrayWrapper = new ByteArrayWrapper(dataBuffer);
byteArrayWrapper.setBytesReadCount(count);
blockingQueue.put(byteArrayWrapper);
}
blockingQueue.put(null); //end of file
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
现在编写器线程读取这些文件内容。
Thread writerThread = new Thread(new Runnable() {
@Override
public void run() {
try {
while(true) {
ByteArrayWrapper byteWrapper = blockingQueue.take();
if(null == byteWrapper) break;
bufferedOutputStream.write(byteWrapper.getBytesRead(), 0, byteWrapper.getBytesReadCount());
}
bufferedOutputStream.flush();
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
线程终于启动了。
readerThread.start();
writerThread.start();
理论上应该从InputStream中读取文件并保存到目标文件中。但是,实际上,它会生成空白的 pdf 文件。其他时候,它显示无效的pdf格式异常。文件大小与 InputStream 的内容长度匹配。有什么我想念的吗?
我不熟悉ByteArrayWrapper
。它是否只包含对数组的引用,就像这样?
public class ByteArrayBuffer {
final private byte[] data;
public ByteArrayBuffer(byte[] data) {
this.data = data;
}
public byte[] getBytesRead() {
return data;
}
/*...etc...*/
}
如果是的话。这就是问题所在:所有 ByteArrayWrapper
对象都由同一个数组支持。被作者反复覆盖。尽管 BlockingQueue
努力将每个对象从一个线程安全地发布到另一个线程。
最简单的修复方法可能是使 ByteArrayWrapper
有效不可变,即在将其发布到另一个线程后不要更改它。在构造时复制数组是最简单的:
public ByteArrayWrapper(byte[] data) {
this.data = Arrays.copyOf(data, data.length);
}
另一个问题是 "BlockingQueue does not accept null elements"(参见 BlockingQueue docs),因此 "end of input" 标记值不起作用。将 null
替换为
private static ByteArrayWrapper END = new ByteArrayWrapper(new byte[]{});
在适当的地方会解决这个问题。
通过对代码副本进行这些更改,我能够检索到 PDF 文件的忠实副本。
尽量使用Android DownloadManager
(http://developer.android.com/reference/android/app/DownloadManager.html)它用来在后台处理long-运行 HTTP请求。
这里不需要考虑收到的字节数,进度会显示在通知栏中。
这里有一个很好的教程:http://blog.vogella.com/2011/06/14/android-downloadmanager-example/