码头没有发送内容长度的标题

发布于 2025-02-06 03:08:50 字数 2989 浏览 2 评论 0原文

我在嵌入式码头中有一个servlet,可以为某些文件提供下载。

我使用的代码如下:

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

出于某种原因,当我从Chrome访问此servlet时,下载开始,但是我看不到下载百分比或文件的总尺寸,以及当我检查请求的响应标题时,content-Length不存在,而是传输编码:块是。 我尝试删除content-disposition标题,但结果是相同的。

我还尝试设置一个伪造的内容长度标头,而不是file.length(),然后将其设置为Response.setContentLengthLong(10000);(该文件是多个GB)。下载在这些10k字节之后停止。

另一个注意事项,在调试时,响应对象确实具有内容长度标头,但由于某种原因,它会自动删除或覆盖。

有什么问题?

添加响应后的更新.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>

I have a servlet in embedded Jetty that provides downloads for certain files.

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.
I have tried removing the Content-Disposition header, but the result is the same.

I also tried to set a fake content length header instead of file.length() and set it to response.setContentLengthLong(10000); (the file is several GB). The download stops after those 10k bytes.

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():

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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

绿光 2025-02-13 03:08:50

当您使用response.setBuffersize(5242880);重置任何处理输出缓冲/流/缓存/缓存的标头(包括contem> content> content> content> content> content> content-Length)。

删除该设置,网络接口MTU上的任何值都是毫无意义的资源浪费,并且不会导致任何比默认值(32K)更快的传输速率。

高值基本上导致填充本地JVM缓冲区,然后被阻塞,而将其写入MTU大小的块网络。

自己检查一下,我敢打赌,接下来它不会超过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());
    }
}

如果您想将标头固定在石头上,请在开始发送响应之前提交标头。

使用response.flushbuffer()在调用reverse.getOutputStream()

一个示例,说明该httpservlet如何在embedded-jetty-jetty-serving-serving-serving-huge-files中找到 httpps:

//github.com/jetty-project/embedded-- JETTY-SERVING-HUGE-FILE

近距离观察 myFileSservlet 查看其工作原理。

您还可以查看 servertest 看到它确实在两个用例中都发送contem> content> content> content> content> content> defaultservlet并使用 myFileSservlet

When you used response.setBufferSize(5242880); you reset any header that deals with the output buffer/stream/cache (including 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).

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.

Check it yourself, I bet it wont ever be over 65536 (and more than often only 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()

An example of how this HttpServlet would look can be found in the embedded-jetty-serving-huge-files project at

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

Look closely at the MyFilesServlet to see how it works.

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)

熊抱啵儿 2025-02-13 03:08:50

在这种情况下,问题不是码头或代码的任何部分,而是在Apache VirtualHost配置中。删除以下行后,它开始按预期发送内容长度的标头:

   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

In this case the problem was not jetty or any part of the code, but it was in the apache virtualhost configuration. After i removed the following lines it started sending the Content-Length header as expected:

   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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文