3

我正在从互联网上单独下载一个文件。像3个地区。假设我必须下载大小为 1024kB 的文件,并且我已将区域设置为 0-340kB、341 - 680kB 和 681kB - 1024 kB。我有每个部分的单独线程。但是,我现在遇到的问题是将下载的文件内容写入单个文件。

由于我们有 3 个线程,每个线程都会依次下载需要写入文件的部分。

我怎样才能做到这一点?我想有 3 个临时文件并写入它们。写入所有文件后,我必须逐个文件读取文件并写入单个文件。我觉得这是一种开销。还有其他更好的方法吗?

提前致谢。

4

1 回答 1

0

需要明确的是,我不相信这种方法实际上会提高下载速度。但是,如果您从多个镜像下载相同的文件,它可能会提供更一致的下载速度。

首先,如果您的文件不是太大,您可以在写出之前缓冲所有文件。所以分配一个所有线程都可以访问的缓冲区:

byte[] buf = new byte[fileSize];

现在您创建一个合适的线程类型:

public class WriterThread extends Thread
{
    byte[] buf;
    int write_pos, write_remaining;

    public WriterThread(byte[] buf, int start, int len)
    {
        this.buf = buf;
        this.write_pos = start;
        this.write_remaining = len;
    }

    @Override
    public void run()
    {
        try (Socket s = yourMethodForSettingUpTheSocketConnection();
             InputStream istream = s.getInputStream()) {
            while (this.write_remaining > 0) {
                int read = istream.read(this.buf, this.write_pos, this.write_remaining);
                if (read == -1) error("Not enough data received");
                this.write_remaining -= read;
                this.write_pos += read;
            }
            // otherwise you are done reading your chunk!
        }
    }
}

WriterThread现在,您可以使用合适的起点和长度来启动尽可能多的这些对象。例如,对于大小为 6000 字节的文件:

byte[] buf = new byte[6000];
WriterThread t0 = new WriterThread(buf, 0, 3000);
WriterThread t1 = new WriterThread(buf, 3000, 3000);
t0.start();
t1.start();
t0.join();
t1.join();
// check for errors

请注意这里的重要一点:每个 WriterThreads 都有一个对完全相同的缓冲区的引用,只是它开始写入的偏移量不同。当然,您必须确保yourMethodForSettingUpTheSocketConnection请求数据从 offset 开始this.write_pos;您如何做到这一点取决于您使用的网络协议,并且超出了您的要求。

如果您的文件太大而无法放入内存,则此方法将不起作用。相反,您必须使用(较慢的)方法,首先创建一个大文件,然后写入该文件。虽然我没有尝试过,但您应该可以使用java.nio.file.File.newByteChannel()' to set up a suitableSeekableByteChannel as your output file. If you create such aSeekableByteChannel sbc`,然后您应该可以这样做

sbc.location(fileSize - 1); // move to the specified position in the file
sbc.write(java.nio.ByteBuffer.allocate(1)); // grow the file to the expected final size

然后SeekableByteChannel每个线程使用一个不同的对象,指向磁盘上的同一个文件,并使用该SeekableByteChannel.location(int)方法设置写入开始位置。你需要一个临时byte[]的,你可以围绕它包装一个ByteBuffer(via ByteBuffer.wrap()),但除此之外,该策略类似于上面的:

thread_local_sbc.location(this.write_pos);

然后 eachthread_local_sbc.write()将写入从 . 开始的文件this.write_pos

于 2013-11-26T20:33:51.453 回答