你好我尊敬的老年人:)
我的目标:通过在Java中使用多线程来下载URL资源,即使用URL中的多线程,即将单个文件下载到多个部分(非常类似于IDM的工作方式)&在下载结束时,将所有这些组合成1个最终文件。例如,如果一个Image是260KB,我想将它下载到2个线程中,1st Thread
应该从 0KB下载到130KB & 2nd Thread
应从131KB to 260KB
下载。
技术使用: Java,RandomAccessFile,MultiThreading,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();
}
&安培;现在,
THREAD CLASS
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
答案 0 :(得分:2)
您正尝试从多个线程写入相同的file
对象。当你搜索()或写()时,它只有一个视图,表明它在哪里。
如果你想同时在不同的地方写同一个,每个线程必须拥有它自己的RandomAccessFile对象,即使它们都指向同一个底层文件。
顺便说一句:更改IO绑定进程的线程优先级可能会做的很少(除非您是管理员,否则没有)
答案 1 :(得分:1)
首先:使用ExecutorService
代替原始线程;你将更容易管理你的工作。
第二:为什么每次文件大小/ 2?更好(imho)将使用固定的块大小。
第三:使用FileChannel
并以块的形式映射文件(使用FileChannel.open()
获取频道)。
四:使用java.nio和try-with-resources,因为你使用Java 7;)
以下是如何从偏移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);
}
这基本上就是你的一个工人的内容。
答案 2 :(得分:0)
要接收文件,文件必须至少是您说话位置的长度。因此,您必须首先使用结果大小创建文件,然后根据您的搜索位置覆盖数据。