我正在用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。
感谢任何帮助!
答案 0 :(得分:0)
transferTo()
和transferFrom()
,直到它们停止返回正值。它们未被指定在单个操作中执行整个转换。您需要每次都按前一个返回值调整偏移量。