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

2023-12-02

我尝试修改@BalusC优秀教程here发送 gzip 压缩文件。这是一个工作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.
    }
}

你必须编辑FILENAME and URL字段并在 URL 中设置 servlet - 它doPost()方法是:

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    Collection<Part> 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 文件打开为存档,但当我尝试提取文件时insidegzip 存档它说它已损坏 - 尽管它确实提取了(确实已损坏)文件)。尝试使用各种文件 - 较大的文件最终会在某些时候损坏,较小的文件会提取为空 - 存档内较大文件的报告大小远大于实际大小,而较小的文件为 0。我标记了以下部分需要注意// ***。我可能会错过一些连接配置,或者我对流进行 gzip 压缩的方式可能完全错误,或者......?
尝试调整连接属性、缓冲区、缓存等,但无济于事


你需要打电话

((GZIPOutputStream)output2).finish();

冲洗前。请参阅 javadochere。它指出

完成将压缩数据写入输出流而不关闭 底层流。应用多个过滤器时使用此方法 连续到相同的输出流。

这就是你正在做的事情。所以

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(); // 

对主题对同一输出流连续应用多个过滤器,我是这样理解的:

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

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

因此,您可以看到如何使用不同的过滤器连续编写不同的部分。想想PrintWriter作为未过滤的过滤器,您提供的任何内容都会直接写入。这GZIPOutputStream是一个 gzip 过滤器,它对给定的字节进行编码(gzip)。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当我使用 GZIPOutputStream 将文件发布到 servlet 时文件已损坏 的相关文章

  • Java中有没有一种方法可以通过名称实例化一个类?

    我正在寻找问题 从字符串名称实例化一个类 https stackoverflow com questions 9854900 instantiate an class from its string name它描述了如何在有名称的情况下实例
  • 如何让 BlazeDS 忽略属性?

    我有一个 java 类 它有一个带有 getter 和 setter 的字段 以及第二对 getter 和 setter 它们以另一种方式访问 该字段 public class NullAbleId private static final
  • 不同帐户上的 Spring Boot、JmsListener 和 SQS 队列

    我正在尝试开发一个 Spring Boot 1 5 应用程序 该应用程序需要侦听来自两个不同 AWS 帐户的 SQS 队列 是否可以使用 JmsListener 注解创建监听器 我已检查权限是否正确 我可以使用 getQueueUrl 获取
  • 如何在java中将一个数组列表替换为另一个不同大小的数组列表

    我有两个大小不同的数组列表 如何从此替换 ArrayList
  • Pig Udf 显示结果

    我是 Pig 的新手 我用 Java 编写了一个 udf 并且包含了一个 System out println 其中的声明 我必须知道在 Pig 中运行时该语句在哪里打印 假设你的UDF 扩展了 EvalFunc 您可以使用从返回的 Log
  • jQuery AJAX 调用 Java 方法

    使用 jQuery AJAX 我们可以调用特定的 JAVA 方法 例如从 Action 类 该 Java 方法返回的数据将用于填充一些 HTML 代码 请告诉我是否可以使用 jQuery 轻松完成此操作 就像在 DWR 中一样 此外 对于
  • 在接口中使用默认方法是否违反接口隔离原则?

    我正在学习 SOLID 原则 ISP 指出 客户端不应被迫依赖于他们所使用的接口 不使用 在接口中使用默认方法是否违反了这个原则 我见过类似的问题 但我在这里发布了一个示例 以便更清楚地了解我的示例是否违反了 ISP 假设我有这个例子 pu
  • 没有 Spring 的自定义 Prometheus 指标

    我需要为 Web 应用程序提供自定义指标 问题是我不能使用 Spring 但我必须使用 jax rs 端点 要求非常简单 想象一下 您有一个包含键值对的映射 其中键是指标名称 值是一个简单的整数 它是一个计数器 代码会是这样的 public
  • jdbc mysql loginTimeout 不起作用

    有人可以解释一下为什么下面的程序在 3 秒后超时 因为我将其设置为在 3 秒后超时 12秒 我特意关闭了mysql服务器来测试mysql服务器无法访问的这种场景 import java sql Connection import java
  • Java ResultSet 如何检查是否有结果

    结果集 http java sun com j2se 1 4 2 docs api java sql ResultSet html没有 hasNext 方法 我想检查 resultSet 是否有任何值 这是正确的方法吗 if resultS
  • Java 和 Python 可以在同一个应用程序中共存吗?

    我需要一个 Java 实例直接从 Python 实例数据存储中获取数据 我不知道这是否可能 数据存储是否透明 唯一 或者每个实例 如果它们确实可以共存 都有其单独的数据存储 总结一下 Java 应用程序如何从 Python 应用程序的数据存
  • 尝试将 Web 服务部署到 TomEE 时出现“找不到...的 appInfo”

    我有一个非常简单的项目 用于培训目的 它是一个 RESTful Web 服务 我使用 js css 和 html 创建了一个客户端 我正在尝试将该服务部署到 TomEE 这是我尝试部署时遇到的错误 我在这里做错了什么 刚刚遇到这个问题 我曾
  • logcat 中 mSecurityInputMethodService 为 null

    我写了一点android应显示智能手机当前位置 最后已知位置 的应用程序 尽管我复制了示例代码 并尝试了其他几种解决方案 但似乎每次都有相同的错误 我的应用程序由一个按钮组成 按下按钮应该log经度和纬度 但仅对数 mSecurityInp
  • 为什么 Java 8 不允许非公共默认方法?

    让我们举个例子 public interface Testerface default public String example return Hello public class Tester implements Testerface
  • 如何使用 jUnit 将测试用例添加到套件中?

    我有 2 个测试类 都扩展了TestCase 每个类都包含一堆针对我的程序运行的单独测试 如何将这两个类 以及它们拥有的所有测试 作为同一套件的一部分执行 我正在使用 jUnit 4 8 在 jUnit4 中你有这样的东西 RunWith
  • 干净构建 Java 命令行

    我正在使用命令行编译使用 eclipse 编写的项目 如下所示 javac file java 然后运行 java file args here 我将如何运行干净的构建或编译 每当我重新编译时 除非删除所有内容 否则更改不会受到影响 cla
  • 找不到符号 NOTIFICATION_SERVICE?

    package com test app import android app Notification import android app NotificationManager import android app PendingIn
  • 如何使用mockito模拟构建器

    我有一个建造者 class Builder private String name private String address public Builder setName String name this name name retur
  • CamcorderProfile.videoCodec 返回错误值

    根据docs https developer android com reference android media CamcorderProfile html 您可以使用CamcorderProfile获取设备默认视频编解码格式 然后将其
  • Java中super关键字的范围和使用

    为什么无法使用 super 关键字访问父类变量 使用以下代码 输出为 feline cougar c c class Feline public String type f public Feline System out print fe

随机推荐