码头没有发送内容长度的标题
我在嵌入式码头中有一个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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
当您使用
response.setBuffersize(5242880);
重置任何处理输出缓冲/流/缓存/缓存的标头(包括contem> content> content> content> content> content> content-Length
)。删除该设置,网络接口MTU上的任何值都是毫无意义的资源浪费,并且不会导致任何比默认值(32K)更快的传输速率。
高值基本上导致填充本地JVM缓冲区,然后被阻塞,而将其写入MTU大小的块网络。
自己检查一下,我敢打赌,接下来它不会超过65536(并且通常只有1500),
如果您想将标头固定在石头上,请在开始发送响应之前提交标头。
使用
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 (includingContent-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)
Next, if you want to fix your headers in stone, commit the headers before you start sending the response.
Use
response.flushBuffer()
before you callresponse.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 sendContent-Length
in both use cases (UsingDefaultServlet
and using theMyFilesServlet
)在这种情况下,问题不是码头或代码的任何部分,而是在Apache VirtualHost配置中。删除以下行后,它开始按预期发送内容长度的标头:
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: