[英]Incorrect File download through Multithreading in Java using RandomAccessFile
你好,我尊敬的老年人:)
我的目标:通过使用Java中的多线程来下载给定URL的URL资源,即将单个文件下载为多个文件(类似于IDM的方式),并在下载结束时将所有文件合并为一个最终文件。 例如,如果一个映像为260KB,我想将其下载到2个线程中, 1st Thread
应从0KB下载到130KB , 2nd Thread
应从131KB to 260KB
下载到131KB to 260KB
。
使用的技术: Java,RandomAccessFile,多线程,InputStreams
问题:此代码适用于1个线程,即,如果我运行一个从0到260KB开始的线程。 但是,当我尝试分批下载时,会发生以下错误:
它将下载额外的垃圾KB,即对于260KB文件,将下载300 + KB。
要么
有时,确切的260KB已下载,但文件已损坏,我无法打开图像。
请帮我。 自整周以来,我已经尝试了很多次,但似乎无法理解问题所在。
初始代码
void InitiaeDownload
{
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
uc.connect();
long fileSize = uc.getContentLengthLong();
System.out.println("File Size = "+ fileSize );
uc.disconnect();
//------------------
long chunkSize = (long) Math.ceil(fileSize/2);
long startFrom = 0;
long endRange = (startFrom + chunkSize) - 1;
System.out.println("Chunk Size = " + chunkSize);
System.out.println("Part 1 :: Start = " + startFrom + "\tEnd To = " + endRange);
Thread t1 = new MyThread(url, file, startFrom, endRange);
t1.start();
startFrom += chunkSize;
long temp = endRange + chunkSize;
endRange = temp + (fileSize-temp); //also add remaining bytes
System.out.println("Part 2 :: Start = " + startFrom + "\tEnd To = " + endRange );
Thread t2 = new MyThread(url, file, startFrom, endRange);
t2.start();
}
现在
螺纹类
class MyThread extends Thread {
private URL url;
private long startFrom;
private long range;
private InputStream inStream;
private RandomAccessFile file;
public MyThread(URL url, RandomAccessFile file, long startFrom, long range) //parameterized constructor
{
this.url = url;
this.file = file;
this.startFrom = startFrom;
this.range = range;
}
public void run() {
System.out.println("Thread Running..");
Thread.currentThread().setPriority(MAX_PRIORITY);
System.setProperty("http.proxyHost", "192.168.10.50");
System.setProperty("http.proxyPort", "8080");
HttpURLConnection uc = null;
try {
uc = (HttpURLConnection) url.openConnection();
uc.setRequestProperty("Range", "bytes="+startFrom+"-"+range);
uc.connect();
inStream = uc.getInputStream();
System.out.println("Starting Download");
int bytesRead = 0;
byte[] buffer = new byte[ (int) (range-startFrom) ];
file.seek(startFrom); //adjusted start of file
while( (bytesRead = inStream.read(buffer) ) != -1 ) {
file.write(buffer, 0, bytesRead);
}
System.err.println("Download Completed!");
uc.disconnect();
}
catch(IOException e) {
System.err.println("Exception in " + Thread.currentThread().getName() + "\t Exception = " + e );
}
} ///END OF run()
} ////END OF MyThread Class
您试图从多个线程写入同一file
对象。 当您seek()或write()时,它只对执行位置有一个看法。
如果要一次在不同的位置写入相同的内容,则每个线程都必须具有其自己的RandomAccessFile对象,即使它们都指向同一个基础文件也是如此。
顺便说一句:更改IO绑定进程的线程优先级可能做的很少(除非您是管理员,否则什么也不会做)
首先:使用ExecutorService
代替原始线程; 您将可以更轻松地管理工作。
第二:为什么每次文件大小为2? 更好的(恕我直言)将使用固定的块大小。
第三:使用FileChannel
并成块地映射文件(使用FileChannel.open()
获取通道)。
四:由于使用Java 7,因此请使用java.nio和try-with-resources;)
这是在文件中从偏移量1000(包括)写入偏移量2000(不包括)的方式。 请注意,文件大小会根据需要扩展!
// "channel" is your FileChannel.
// Argument to the worker: the channel, the offset, the size to write
final ByteBuffer buf = channel.map(FileChannel.MapMode.READ_WRITE, 1000L, 1000L);
try (
final InputStream in = uc.getInputStream();
final ReadableByteChannel inChannel = Channels.newChannel(in);
) {
inChannel.read(buf); // returns the number of bytes read
channel.force(false);
}
这基本上就是您的一名工人的内容。
要隐藏文件,文件必须至少等于您说话的位置的长度。 因此,您必须首先创建具有结果大小的文件,然后根据您的搜索位置覆盖数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.