当我使用 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 时文件已损坏 的相关文章

  • Math.random() 解释

    这是一个非常简单的 Java 尽管可能适用于所有编程 问题 Math random 返回 0 到 1 之间的数字 如果我想返回零到百之间的整数 我会这样做 int Math floor Math random 101 在一到一百之间 我会这
  • 如何使用 Java 以独立于平台的方式读取 Windows 共享驱动器?

    如何使用 Java 从 Windows 共享驱动器中读取数据 以便执行读取的 Java 代码可以在任何平台上同样正确地运行 您可以使用JCIFS http jcifs samba org 使用纯 Java 代码访问 SMB CIFS 共享
  • 在 JTable 中使用自定义 TablecellRenderer

    我是 Java 新手 我创建了一个JTable 就是这样addRow当我尝试向表中添加一行时 该方法有效 private void addTableRow String type String name String rank String
  • 如何在 Java 中验证从 Azure AD B2C 生成的 JWT 令牌?

    我正在寻找 Java 代码示例来验证 Azure AD B2C 令牌 我们可以使用哪些依赖项 所有 JWT 令牌的 JWT 令牌验证步骤或代码是否相同还是会有所不同 我们的项目中没有使用 Spring Security 有大量的图书馆her
  • CompletableFuture:whenCompleteAsync() 不允许我重新抛出异常

    我是 CompletableFuture 世界的新手 我正在尝试做一些负面测试 以允许我故意抛出异常的方式 该异常将决定通过 失败 这是代码片段 protected CompletableFuture
  • SWT:如何进行高质量图像调整大小

    我的应用程序需要调整 ImageData 的大小 不幸的是 我还没有通过 GC 开启抗锯齿和高插值 或 ImageData scaledTo 获得我想要的结果 生成的图像质量太低 无法接受 进行高质量 ImageData 调整大小的最佳方法
  • cygwin有java sdk吗?

    cygwin有java sdk吗 如果有一个使用 cygwin 文件系统和 X windows 进行显示的本机 cygwin 实现 那就太好了 不幸的是我不知道这样的版本 我认为移植 OpenJDK 也需要付出很大的努力 但我还没有尝试过
  • 将倒计时器从 10 秒改为 1 秒

    我有一个倒计时器 它以 1 秒的增量从 10000 毫秒倒计时到 0 毫秒 以使按钮在 10 秒后可单击 尽管计时器是准确的并且按照代码的说明执行操作 但我想更改秒的表示方式 但我不知道如何更改 java void startTimer c
  • 如何在Java中使用我的密码加密和解密字符串(PC而非移动平台)? [复制]

    这个问题在这里已经有答案了 我想加密一个字符串然后将其放入文件中 当我想要的时候也想解密它 我不需要很强的安全性 我只是想让其他人更难获取我的数据 我尝试了几种方法 这是这些 Md5加密 如何在 Android 中对字符串进行哈希处理 ht
  • 如何通过两跳 SSH 隧道使用 JProfiler

    我正在尝试将 JProfiler 连接到在我将调用的服务器上运行的 JVMremote 该服务器只能从我的工作站访问 local 通过我将调用的另一台服务器middle 我的计划是将 JProfiler 连接到remote是这样的 安装 J
  • Spring批量写入器限制

    我正在工作 Spring Batch 项目 从数据库读取记录然后写入rabbitmq 然后发送到HTTP消息网关 网关有150TPS我需要将我的应用程序限制为 150TPS 有没有办法带弹簧批的油门或者还有其他更好的方法吗 你能行的 在 S
  • [TYPE] 类型的 Bean 'x' 不符合所有 BeanPostProcessors 的处理条件

    我有一个ResourceAspect class Component Aspect public class ResourceAspect Before execution public public void resourceAccess
  • 使用嵌入式 Jetty 7 发布 JAX-WS 端点

    有人可以帮忙吗 我想使用嵌入式 Jetty 7 作为端点 这是我尝试过的 public class MiniTestJetty WebService targetNamespace http public static class Calc
  • MongoDb Spring 在嵌套对象中查找

    我正在使用 Spring Data Mongodb 和这样的文档 id ObjectId 565c5ed433a140520cdedd7f attributes 565c5ed433a140520cdedd73 333563851 list
  • 在仔细锁定但不受信任的代码上使用 Thread.stop()

    我知道Thread stop 已被弃用 并且有充分的理由 它通常不安全 但这并不意味着它是never安全 据我所知 在我想要使用它的上下文中它是安全的 而且 据我所知 我别无选择 上下文是一个两人策略游戏的第三方插件 以国际象棋为例 第三方
  • 为什么这段代码可以在 Java 7 中运行,而不能在 Java 8 中运行?

    我目前使用 IDE Eclipse 版本 Neon 2 Release 4 6 2 和版本 java Version 8 Update 131 在此代码中 IDE 给出错误 类型不匹配 无法从字节转换为整数 Integer i byte 1
  • 我的代码线程不安全吗?

    我编写了代码来理解 CyclicBarrier 我的应用程序模拟选举 每轮选出得票少的候选人 该候选人从竞争中淘汰以获得胜利 source class ElectoralCommission public volatile boolean
  • FocusEvent 没有获取 JFormattedTextField 的最后一个值,我如何获取它?

    我有两个JFormattedTextField我的物体JFrame目的 我想要通过这些值进行基本数学 加法 JFormattedTextField对象 我希望当焦点丢失第一个或第二个文本字段时发生这种情况 但当 focusLost 事件没有
  • 如何在 logback 中启动时滚动日志文件

    我想配置 logback 来执行以下操作 记录到文件 当文件达到 50MB 时滚动文件 仅保留 7 天的日志 启动时始终生成一个新文件 滚动 除了最后一项 启动卷 外 我一切都正常 有谁知道如何实现这一目标 这是配置
  • 无法取消 GWT 中的重复计时器

    我正在尝试在 GWT 中安排一个重复计时器 它将每一毫秒运行一次 轮询某个事件 如果发现满意 则执行某些操作并取消计时器 我尝试这样做 final Timer t new Timer public void run if condition

随机推荐