Java ZIP文件下载损坏?

时间:2014-05-03 04:29:17

标签: java download zip

我正在用Java编写一个非常基本的HTTP服务器来为Minecraft游戏提供文件(资源包)。资源包以ZIP文件格式分发。原始文件压缩成如下存档:

public void createZIPFile(File file) throws IOException{
    File output = new File(file.getParentFile(), file.getName() + ".zip");
    ZipOutputStream out = new ZipOutputStream(new FileOutputStream(output));

    ZipEntry entry = new ZipEntry("assets/minecraft/sounds/mob/wolf/howl.ogg");
    out.putNextEntry(entry);
    out.write(Files.readAllBytes(file.toPath()));
    out.closeEntry();

    ZipEntry mcMeta = new ZipEntry("pack.mcmeta");
    out.putNextEntry(mcMeta);
    out.write(("{\r\n" + "    \"pack\": {\r\n" + "         \"pack_format\": 1,\r\n" + "         \"description\": \"SoundCraft!\"\r\n" + "     }\r\n" + "}").getBytes());
    out.closeEntry();

    out.finish();
    out.flush();
    out.close();
}

随着JRE7中新的Channels API的引入,我决定使用它而不是传统的while-read循环。 HTTP服务器类如下:

package priv.tenko.soundcraft.http;

import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;

import priv.tenko.soundcraft.SoundCraft;

public class BasicHTTPServer extends Thread {

    private final Logger httpLog = Logger.getLogger("SoundCraft-HTTP");
    private final int port = 25566;
    private AtomicBoolean running = new AtomicBoolean(false);
    private ServerSocketChannel socket;

    public void close(){
        running.set(false);
        try{
            socket.close();
        }catch (IOException e){
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public void run(){
        socket = null;
        running.set(true);

        try{
            httpLog.info("Starting basic HTTP web server on port " + port + "...");
            socket = ServerSocketChannel.open();
            socket.bind(new InetSocketAddress("localhost", port));
            httpLog.fine("Successfully binded! Awaiting requests.");
        }catch (IOException e){
            e.printStackTrace();
        }

        while(running.get()){
            try{
                SocketChannel newConnection = socket.accept();
                newConnection.configureBlocking(true);

                InetSocketAddress address = (InetSocketAddress) newConnection.getRemoteAddress();

                ByteBuffer read = ByteBuffer.allocate(128);
                newConnection.read(read);
                String request = new String(read.compact().array());

                if(!request.startsWith("GET")){
                    httpLog.warning("Recieved non-GET HTTP request. Please look into it? " + address.getAddress().getHostAddress() + ":" + address.getPort() + " tried " + request);
                    newConnection.close();
                    continue;
                }

                request = request.substring(5); // At this point, it should be "somefile.ext HTTP/1.1" or whatever
                request = request.substring(0, request.indexOf(' ')); // Aaaaand now it should be "somefile.ext"
                httpLog.info(newConnection.getRemoteAddress() + " is requesting " + request);
                httpLog.info("Outside URL would look like: " + this.getURLDestination(request));
                File target = new File(SoundCraft.getSoundDirectory(), request);
                if(!target.exists()){
                    httpLog.info("Couldn't find the requested file. Terminating connection.");
                    newConnection.close();
                    continue;
                }

                newConnection.write(ByteBuffer.wrap(getOkayMessage(target).getBytes()));

                try(FileInputStream stream = new FileInputStream(target);){
                    stream.getChannel().transferTo(0, target.length(), newConnection);
                }

                newConnection.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }

        try{
            socket.close();
        }catch (Throwable e){
            e.printStackTrace();
        }
    }

    public String getURLDestination(String file){
        try{
            return "http://localhost:" + port + "/" + file;
        }catch (Exception e){
            e.printStackTrace();
            return "";
        }
    }

    public String getOkayMessage(File file){
        return "HTTP/1.0 200 OK\r\n" + "Content-Length: " + file.length() + "\r\n" + "Connection: close\r\n" + "Server: SoundCraft-HTTP\r\n" + "Content-Type: text/html\r\n" + "Content-Transfer-Encoding: BINARY\r\n";
    }
}

但是,当我使用此方法,并且客户端下载ZIP文件时,ZIP部分损坏。通过在Notepad ++中打开ZIP文件进一步检查显示服务器正在跳过文件的一部分。

我无法发布图像,所以this is a direct link发布到引用数据跳过的图像。 This question与此密切相关;我们都遇到了一个涉及缺失数据的问题。但是,相关问题使用传统的while-read循环,而我的使用新的Channels API。

感谢任何帮助!

1 个答案:

答案 0 :(得分:0)

必须在循环中调用

transferTo()transferFrom(),直到它们停止返回正值。它们未被指定在单个操作中执行整个转换。您需要每次都按前一个返回值调整偏移量。