当我使用GZIPOutputStream将其发布到servlet时,文件已损坏

我试着在这里修改@BalusC优秀教程来发送gziped压缩文件。 这是一个有效的java类:

import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.zip.GZIPOutputStream; public final class NetworkService { // *** EDIT THOSE AS APROPRIATE private static final String FILENAME = "C:/Dropbox/TMP.txt"; private static final String URL = "http://192.168.1.64:8080/DataCollectionServlet/"; // *** END EDIT private static final CharSequence CRLF = "\r\n"; private static boolean isServerGzip = true; // *** private static String charsetForMultipartHeaders = "UTF-8"; public static void main(String[] args) { HttpURLConnection connection = null; OutputStream serverOutputStream = null; try { File file = new File(FILENAME); final String boundary = Long .toHexString(System.currentTimeMillis()); connection = connection(true, boundary); serverOutputStream = connection.getOutputStream(); try { flushMultiPartData(file, serverOutputStream, boundary); } catch (IOException e) {} System.out.println(connection.getResponseCode()); // 200 } catch (IOException e) { // Network unreachable : not connected // No route to host : probably on an encrypted network // Connection timed out : Server DOWN } finally { if (connection != null) connection.disconnect(); } } private static HttpURLConnection connection(boolean isMultiPart, String boundary) throws MalformedURLException, IOException { HttpURLConnection connection = (HttpURLConnection) new URL(URL) .openConnection(); connection.setDoOutput(true); // triggers POST connection.setUseCaches(false); // *** no difference connection.setRequestProperty("Connection", "Keep-Alive"); connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) " + "Gecko/20100401"); // *** tried others no difference connection.setChunkedStreamingMode(1024); // *** no difference if (isMultiPart) { if (boundary == null || "".equals(boundary.trim())) throw new IllegalArgumentException("Boundary can't be " + ((boundary == null) ? "null" : "empty")); connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); } return connection; } // ========================================================================= // Multipart // ========================================================================= private static void flushMultiPartData(File file, OutputStream serverOutputStream, String boundary) throws IOException { PrintWriter writer = null; try { // true = autoFlush, important! writer = new PrintWriter(new OutputStreamWriter(serverOutputStream, charsetForMultipartHeaders), true); appendBinary(file, boundary, writer, serverOutputStream); // End of multipart/form-data. writer.append("--" + boundary + "--").append(CRLF); } finally { if (writer != null) writer.close(); } } private static void appendBinary(File file, String boundary, PrintWriter writer, OutputStream output) throws FileNotFoundException, IOException { // Send binary file. writer.append("--" + boundary).append(CRLF); writer.append( "Content-Disposition: form-data; name=\"binaryFile\"; filename=\"" + file.getName() + "\"").append(CRLF); writer.append( "Content-Type: " // *** + ((isServerGzip) ? "application/gzip" : URLConnection .guessContentTypeFromName(file.getName()))) .append(CRLF); writer.append("Content-Transfer-Encoding: binary").append(CRLF); writer.append(CRLF).flush(); InputStream input = null; OutputStream output2 = output; if (isServerGzip) { output2 = new GZIPOutputStream(output); } try { input = new FileInputStream(file); byte[] buffer = new byte[1024]; // *** tweaked, no difference for (int length = 0; (length = input.read(buffer)) > 0;) { output2.write(buffer, 0, length); } output2.flush(); // Important! Output cannot be closed. Close of // writer will close output as well. } finally { if (input != null) try { input.close(); } catch (IOException logOrIgnore) {} } writer.append(CRLF).flush(); // CRLF is important! It indicates end of // binary boundary. } } 

您必须编辑FILENAMEURL字段并在URL中设置servlet – 其doPost()方法是:

 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Collection parts = req.getParts(); for (Part part : parts) { File save = new File(uploadsDirName, getFilename(part) + "_" + System.currentTimeMillis() + ".zip"); final String absolutePath = save.getAbsolutePath(); log.debug(absolutePath); part.write(absolutePath); sc.getRequestDispatcher(DATA_COLLECTION_JSP).forward(req, resp); } } 

现在当isServerGzip字段设置为true时,FILENAME被压缩并发送到服务器,但是当我尝试提取它时它已损坏(我在Windows上使用7z打开gzip文件作为存档但是当我尝试将文件解压缩到内部时它说gzip存档已损坏 – 尽管它确实提取了(确实已损坏的)文件)。 尝试使用各种文件 – 较大的文件最终在某些时候被破坏,较小的文件被提取为空 – 归档中较大文件的报告大小比实际大小大得多,而较小的文件大小为0.我标记了部分需要注意// *** 。 我可能会错过一些连接配置或我的gzipping流的方式可能是完全错误或…?
尝试调整连接属性,缓冲区,缓存等无济于事

你需要打电话

 ((GZIPOutputStream)output2).finish(); 

在冲洗之前。 在这里查看javadoc。 它指出

完成将压缩数据写入输出流而不关闭底层流。 将多个filter连续应用于同一输出流时,请使用此方法。

这是你在做什么。 所以

 for (int length = 0; (length = input.read(buffer)) > 0;) output2.write(buffer, 0, length); } ((GZIPOutputStream)output2).finish(); //Write the compressed parts // obviously make sure output2 is truly GZIPOutputStream output2.flush(); // 

关于将多个filter连续应用于同一输出流的主题 ,我就是这样理解的:

您有一个OutputStream ,它是一个套接字连接,到HTTP服务器。 HttpUrlConnection写入标题然后直接写入正文。 在这种情况下(multipart),您将边界和标题发送为解压缩的字节,压缩文件内容,然后再发送边界。 所以流最终看起来像这样:

  start writing with GZIPOutputStream v |---boundary---|---the part headers---|---gzip encoded file content bytes---|---boundary---| ^ ^ write directly with PrintWriter use PrintWriter again 

因此,您可以看到如何使用不同的filter连续编写不同的部分。 将PrintWriter视为未经过滤的filter,您提供的任何内容都是直接编写的。 GZIPOutputStream是一个gzipfilter,它对(gzips)它给出的字节进行编码。

至于源代码,查看Java JDK安装,你应该有一个包含公共源代码的src.zip文件, java.lang*java.util.* java.io.*javax.*javax.*等。