【发布时间】:2015-08-21 09:42:10
【问题描述】:
我正在尝试创建一个 java 程序,将某些资产文件从 FTP 服务器下载到本地文件。因为我的(免费)FTP 服务器不支持超过几兆字节的文件大小,所以我决定在上传文件时拆分文件,并在程序下载文件时重新组合它们。这行得通,但速度很慢,因为对于每个文件,它必须获取InputStream,这需要一些时间。
我使用的 FTP 服务器可以下载文件而无需实际登录服务器,因此我使用此代码获取 InputStream:
private static final InputStream getInputStream(String file) throws IOException {
return new URL("http://site.website.com/path/" + file).openStream();
}
要获取资产文件一部分的InputStream,我正在使用以下代码:
public static InputStream getAssetInputStream(String asset, int num) throws IOException, FTPException {
try {
return getInputStream("assets/" + asset + "_" + num + ".raf");
} catch (Exception e) {
// error handling
}
}
因为getAssetInputStreams(String, int) 方法需要一些时间来运行(特别是如果文件大小超过一兆字节),我决定将实际下载文件的代码设为多线程。这就是我的问题所在。
final Map<Integer, Boolean> done = new HashMap<Integer, Boolean>();
final Map<Integer, byte[]> parts = new HashMap<Integer, byte[]>();
for (int i = 0; i < numParts; i++) {
final int part = i;
done.put(part, false);
new Thread(new Runnable() {
@Override
public void run() {
try {
InputStream is = FTP.getAssetInputStream(asset, part);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[DOWNLOAD_BUFFER_SIZE];
int len = 0;
while ((len = is.read(buf)) > 0) {
baos.write(buf, 0, len);
curDownload.addAndGet(len);
totAssets.addAndGet(len);
}
parts.put(part, baos.toByteArray());
done.put(part, true);
} catch (IOException e) {
// error handling
} catch (FTPException e) {
// error handling
}
}
}, "Download-" + asset + "-" + i).start();
}
while (done.values().contains(false)) {
try {
Thread.sleep(100);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
File assetFile = new File(dir, "assets/" + asset + ".raf");
assetFile.createNewFile();
FileOutputStream fos = new FileOutputStream(assetFile);
for (int i = 0; i < numParts; i++) {
fos.write(parts.get(i));
}
fos.close();
此代码有效,但并非总是如此。当我在台式计算机上运行它时,它几乎总是有效。不是 100% 的时间,但通常它工作得很好。在我的笔记本电脑上,它的互联网连接要差得多,它几乎从不工作。结果是一个不完整的文件。有时,它会下载文件的 50%。有时,它会下载 90% 的文件,但每次都不同。
现在,如果我将 .start() 替换为 .run(),则代码 100% 可以正常工作,即使在我的笔记本电脑上也是如此。然而,它非常慢,所以我宁愿不使用.run()。
有没有办法可以更改我的代码以使其在多线程中工作?任何帮助将不胜感激。
【问题讨论】:
-
"此代码有效,但并非总是如此。"你这是什么意思?你收到一些例外吗?哪些组件失败并详细说明失败的原因(必要时提供堆栈跟踪)。此外,您的链接 (
http://site.website.com/path/) 指向 HTTP,并且您在访问done和parts时存在竞争条件。 -
好点,我忘了说。它不会抛出异常,只是不会下载整个文件,只是下载其中的一部分。有时大约是文件的一半,有时可能是文件的 90%,有时是完整的文件。
-
“不抛出异常”是指它不抛出未捕获的异常还是根本不抛出任何异常(包括
IOException和FTPException)。另外,为什么要为done使用 HashMap?为什么不只是一个整数来计算完成了多少件。另外我们能看到检测文件是否完成的代码吗? -
“不抛出异常”我的意思是它实际上不会抛出任何异常。程序正常终止,就像刚刚完成下载一样。我将编辑我的原始帖子以显示检测完整文件的代码。
标签: java multithreading download ftp inputstream