简体   繁体   English

Jetty 未发送 Content-Length 标头

[英]Jetty is not sending the Content-Length header

I have a servlet in embedded Jetty that provides downloads for certain files.我在嵌入式 Jetty 中有一个 servlet,可为某些文件提供下载。

The code I use is the following:我使用的代码如下:

try{
    File file = new File(filePath);
    response.setContentLengthLong(file.length());
    response.setHeader("Content-Disposition", "attachment; filename=myfilename.mkv");
    response.setContentType(Files.probeContentType(file.toPath()));
    response.setBufferSize(5242880);
    in = new FileInputStream(file);
    out = response.getOutputStream();

    byte[] bytes = new byte[5242880];
    int bytesRead;

    while ((bytesRead = in.read(bytes)) != -1) {
        try {
            out.write(bytes, 0, bytesRead);
        } catch (EOFException e) {
            logger.debug("Reached end of file, breaking loop");
            break;
        }
    }
} catch ( Exception e ) {
    e.printStackTrace();
}

For some reason, when I access this servlet from Chrome, the download starts, but I do not see the download percentage nor the total size of the file, and when I check the response headers of the request, Content-Length is not there, but Transfer-Encoding: Chunked is.出于某种原因,当我从 Chrome 访问此 servlet 时,下载开始,但我看不到下载百分比或文件的总大小,当我检查请求的响应标头时, Content-Length不存在,但是Transfer-Encoding: Chunked是。 I have tried removing the Content-Disposition header, but the result is the same.我试过删除Content-Disposition标头,但结果是一样的。

I also tried to set a fake content length header instead of file.length() and set it to response.setContentLengthLong(10000);我还尝试设置一个虚假的内容长度标头而不是file.length()并将其设置为response.setContentLengthLong(10000); (the file is several GB). (文件为几 GB)。 The download stops after those 10k bytes.下载在这 10k 字节之后停止。

Another note, while debugging, the response object does have the content length header, but for some reason it is deleted or overwritten automatically.另请注意,在调试时,响应对象确实具有内容长度标头,但由于某种原因,它被自动删除或覆盖。

What could the problem be?问题可能是什么?

Update after adding response.flushBuffer() :添加 response.flushBuffer() 后更新

String filePath = "path/to/file";
try {
    // Starting the actual stream. It will handle videos differently from other files, in order to support streaming
    Path file = Paths.get(filePath);
    response.setContentLengthLong(Files.size(file));
    response.setHeader("Content-Disposition", "attachment; filename=\"" + fileEntity.fileName + fileEntity.extension + "\"");
    response.setContentType(Files.probeContentType(file));
    response.flushBuffer();

    final int bufferSize = response.getBufferSize();
    try (InputStream in = Files.newInputStream(file)) {
        ServletOutputStream out = response.getOutputStream();

        byte[] bytes = new byte[bufferSize];
        int bytesRead;

        while ((bytesRead = in.read(bytes)) != -1) {
            out.write(bytes, 0, bytesRead);
        }
    }
} catch (Exception e) {
    logger.error(e.getMessage());
}

Here are my jetty dependencies: 这是我的码头依赖项:
 <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> <version>11.0.6</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> <version>11.0.6</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlets</artifactId> <version>11.0.6</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-util</artifactId> <version>11.0.6</version> </dependency>

When you used response.setBufferSize(5242880);当你使用response.setBufferSize(5242880); you reset any header that deals with the output buffer/stream/cache (including Content-Length ).您重置任何处理输出缓冲区/流/缓存(包括Content-Length )的标头。

Remove that setting, any value over the MTU of your networking interface is pointless waste of resources and does not result in any faster transfer rate than the default (which is 32k).删除该设置,网络接口的 MTU 上的任何值都是毫无意义的资源浪费,并且不会导致比默认值(32k)更快的传输速率。

A value that high is basically resulting in the local JVM buffer being filled, then blocked, while its being written to the network in MTU sized chunks.如此高的值基本上会导致本地 JVM 缓冲区被填充,然后被阻塞,同时它以 MTU 大小的块写入网络。

Check it yourself, I bet it wont ever be over 65536 (and more than often only 1500)自己检查一下,我敢打赌它永远不会超过 65536(而且通常只有 1500)

package net;

import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collection;
import java.util.Collections;

public class WhatIsMyMTU {
    public static void main(String[] args) throws SocketException{
        Collection<NetworkInterface> ifaces = Collections.list(NetworkInterface.getNetworkInterfaces());
        for (NetworkInterface iface : ifaces)
            System.out.printf("iface: %s - MTU=%d%n", iface, iface.getMTU());
    }
}

Next, if you want to fix your headers in stone, commit the headers before you start sending the response.接下来,如果您想固定您的标头,请在开始发送响应之前提交标头。

Use response.flushBuffer() before you call response.getOutputStream()在调用 response.getOutputStream( response.flushBuffer() response.getOutputStream()

An example of how this HttpServlet would look can be found in the embedded-jetty-serving-huge-files project at这个 HttpServlet 的外观示例可以在 Embedded-jetty-serving-huge-files 项目中找到

https://github.com/jetty-project/embedded-jetty-serving-huge-files https://github.com/jetty-project/embedded-jetty-serving-huge-files

Look closely at the MyFilesServlet to see how it works.仔细查看MyFilesServlet以了解它是如何工作的。

You can also look at the ServerTest to see that it does send Content-Length in both use cases (Using DefaultServlet and using the MyFilesServlet )您还可以查看ServerTest以查看它在两种用例中都发送Content-Length (使用DefaultServlet和使用MyFilesServlet

In this case the problem was not jetty or any part of the code, but it was in the apache virtualhost configuration.在这种情况下,问题不在于码头或代码的任何部分,而在于 apache 虚拟主机配置。 After i removed the following lines it started sending the Content-Length header as expected:在我删除以下行后,它开始按预期发送 Content-Length 标头:

   SetOutputFilter DEFLATE
    SetEnvIfNoCase Request_URI  \
        \.(?:gif|jpe?g|png)$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI  \
        \.(?:mp3|wav|wma|au|m4p|snd|mid|wmv|mpg|mpeg|mp4|qt|mov)$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI \.pdf$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI  \
        \.(?:exe|t?gz|zip|gz2|sit|rar)$ no-gzip dont-vary

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM