笔者的公司搭建了一个Nexus服务器,用来管理我们自己的项目Release构件和Site文档.

今天的问题是当用户访问一个Site里的PDF文件的时候,报错说“detected that the network has been closed.”

但是用右键点击,然后另存为,却每次都能成功下载下来.本来这几天公司的网络确实是不稳定,刚开始还真的以为是网络问题.

后来仔细观察了http请求后发现,每次报错后浏览器都还在不停的发送 http range 请求. 我们用的是Adbobe的PDF插件.

  • Chrome问题

Chrome里有一个自带的plugin,可以通过 chrome://plugins 进入plugin配置页面,找到自带的Chrome PDF viewer.

这个Chrome插件发现提供PDF的Nexus服务器支持range请求后,在读取完response的header后会主动abort response.

然后改为通过XHR分批发送Http Range 请求,此时Adbobe的PDF插件就报network已经关闭了。这里的主要原因是两个插件互相冲突,

而且chrome自带的PDF viewer有bug,不能正确组装返回的range response. 解决办法可以是禁用Chrome PDF viewer。

  • Firefox问题/IE问题

老版本的IE和Firefox都没问题,有问题的是IE10,Firefox25. 原因是老版本的IE/Firefox里的adobe插件不会发生http range 请求.

新版的IE/Firefox发生了range请求后,服务器也正常的给了response,结果可笑的是adobe 插件不能正确组装,悲剧。

最后想到的就是禁用range请求.但是在流浪器上是做不到的,因为是插件自己发送的.而Nexus服务器2.7.1是hard code支持这个特性的。

range request是http1.1标准里的特性,支持是理所应当的。 下面是NexusContextServlet.Java的部分代码.红色部分显示了这个特性。

protected void doGetFile(final HttpServletRequest request, final HttpServletResponse response,
final StorageFileItem file) throws IOException
{
// ETag, in "shaved" form of {SHA1{e5c244520e897865709c730433f8b0c44ef271f1}} (without quotes)
// or null if file does not have SHA1 (like Virtual) or generated items (as their SHA1 would correspond to template,
// not to actual generated content).
final String etag;
if (!file.isContentGenerated() && !file.isVirtual()
&& file.getRepositoryItemAttributes().containsKey(StorageFileItem.DIGEST_SHA1_KEY)) {
etag = "{SHA1{" + file.getRepositoryItemAttributes().get(StorageFileItem.DIGEST_SHA1_KEY) + "}}";
// tag header ETag: "{SHA1{e5c244520e897865709c730433f8b0c44ef271f1}}", quotes are must by RFC
response.setHeader("ETag", "\"" + etag + "\"");
}
else {
etag = null;
}
// content-type
response.setHeader("Content-Type", file.getMimeType()); // last-modified
response.setDateHeader("Last-Modified", file.getModified()); // content-length, if known
if (file.getLength() != ContentLocator.UNKNOWN_LENGTH) {
// Note: response.setContentLength Servlet API method uses ints (max 2GB file)!
// TODO: apparently, some Servlet containers follow serlvet API and assume
// contents can have 2GB max, so even this workaround below in inherently unsafe.
// Jetty is checked, and supports this (uses long internally), but unsure for other containers
response.setHeader("Content-Length", String.valueOf(file.getLength()));
} // handle conditional GETs only for "static" content, actual content stored, not generated
if (!file.isContentGenerated() && file.getResourceStoreRequest().getIfModifiedSince() != 0
&& file.getModified() <= file.getResourceStoreRequest().getIfModifiedSince()) {
// this is a conditional GET using time-stamp
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
else if (!file.isContentGenerated() && file.getResourceStoreRequest().getIfNoneMatch() != null && etag != null
&& file.getResourceStoreRequest().getIfNoneMatch().equals(etag)) {
// this is a conditional GET using ETag
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
else {
// NEXUS-5023 disable IE for sniffing into response content
response.setHeader("X-Content-Type-Options", "nosniff"); final List<Range<Long>> ranges = getRequestedRanges(request, file.getLength()); // pour the content, but only if needed (this method will be called even for HEAD reqs, but with content tossed
// away), so be conservative as getting input stream involves locking etc, is expensive
final boolean contentNeeded = "GET".equalsIgnoreCase(request.getMethod());
if (ranges.isEmpty()) {
if (contentNeeded) {
try (final InputStream in = file.getInputStream()) {
sendContent(in, response);
}
}
}
else if (ranges.size() > 1) {
response.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED);
renderer.renderErrorPage(request, response, file.getResourceStoreRequest(), new UnsupportedOperationException(
"Multiple ranges not yet supported!"));
}
else {
final Range<Long> range = ranges.get(0);
if (!isRequestedRangeSatisfiable(file, range)) {
response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
response.setHeader("Content-Length", "0");
response.setHeader("Content-Range", "bytes */" + file.getLength());
return;
}
final long bodySize = range.upperEndpoint() - range.lowerEndpoint();
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
response.setHeader("Content-Length", String.valueOf(bodySize));
response.setHeader("Content-Range",
range.lowerEndpoint() + "-" + range.upperEndpoint() + "/" + file.getLength());
if (contentNeeded) {
try (final InputStream in = file.getInputStream()) {
in.skip(range.lowerEndpoint());
sendContent(limit(in, bodySize), response);
}
}
}
}
}

最后幸好我们的请求在到Nexus之前有一个Apache proxy,根据http1.1标准,range request必须是client先发送range 请求,如果服务器支持

那么才返回状态嘛206和部分数据。所以最后我在proxy中把range header 强行去掉。这样服务器就不会返回206,而是返回200了.

也没有content-range header返回了,所以插件就会按照老实的方式,等待下载完成,然后渲染打开PDF文件.

最后的解决方案就是在Apache中增加如下配置.

LoadModule headers_module modules/mod_headers.so

RequestHeader unset Range

Failed to load PDF in chrome/Firefox/IE的更多相关文章

  1. Chrome系列 Failed to load resource: net::ERR_CACHE_MISS

    在IE/FF下没有该错误提示,但在Chrome下命令行出现如下错误信息: Failed to load resource: net::ERR_CACHE_MISS 该问题是Chrome浏览器开发工具的 ...

  2. atom markdown转换PDF 解决AssertionError: html-pdf: Failed to load PhantomJS module

    atom编辑器markdown转换PDF 解决AssertionError: html-pdf: Failed to load PhantomJS module. You have to set th ...

  3. 怎么解决Chrome浏览器“Failed to load resource: net::ERR_INSECURE_RESPONSE”错误?

    用科学方法安装的arcgisServer,需要修改系统时间,但是修改了系统时间,可能会出各种问题, office 不能正确验证,chrome 不能正常使用,访问网页, 如果直接输入本地地址进行访问的话 ...

  4. Selenium IE6 Failed to load the library from temp directory: C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\IED1C1.tmp

    项目中用到了使用Selenium打开IE浏览器.前期都是基于IE8+.Firefox.Chrome.项目后期开始现场测试时发现大部分客户还都是使用的Windows XP + IE6.结果可想而知,直接 ...

  5. Failed to load resource: net::ERR_CACHE_MISS

    Failed to load resource: net::ERR_CACHE_MISS 译为开发人员工具载入缓存的时候,说找不到资源. 原因是你先打开页面,然后打开chrome的开发人员工具.而页面 ...

  6. java.lang.IllegalStateException: Failed to load ApplicationContext selenium 异常 解决

    WARN <init>, HHH000409: Using org.hibernate.id.UUIDHexGenerator which does not generate IETF R ...

  7. atom markdown报错:AssertionError: html-pdf: Failed to load PhantomJS module.

    今天安装markdown-pdf之后运行的时候报错: AssertionError: html-pdf: Failed to load PhantomJS module. You have to se ...

  8. Failed to load slave replication state from table mysql.gtid_slave_pos: 1146: Table 'mysql.gtid_slave_pos' doesn't exist

    http://anothermysqldba.blogspot.com/2013/06/mariadb-1003-alpha-install-on-fedora-17.html MariaDB 10. ...

  9. elasticsearch按照配置时遇到的一些坑 [Failed to load settings from [elasticsearch.yml]]

    这里整理几个空格引起的问题. 版本是elasticsearch-2.3.0 或者elasticsearch-rtf-master Exception in thread "main" ...

随机推荐

  1. asp.net 微信企业号办公系统-流程设计--流程步骤设置-事件设置

    事件设置是设置当前步骤在提交前后或退回前后要执行的一些操作(该事件为服务器事件). 事件格式为:dll名称.命名空间名称.类名.方法名,这里不需要写括号和参数,处理时会自动带上当前流程实例的相关参数. ...

  2. jsp页面中的代码执行加载顺序介绍

    1. java是在服务器端运行的代码,jsp在服务器的servlet里运行,而javascript和html都是在浏览器端运行的代码.所以加载执行顺序是是java>jsp>js. 2. j ...

  3. Java实现FTP上传下载功能

    Java FTP客户端工具包很多,在此我选用的Apache的FTPClient.这个包的获取可以通过http://commons.apache.org/net/来获取,我使用的是最新的commons- ...

  4. rbegin 和 end 区别

    在使用C++的STL时,经常会用到迭代器,那么不得不搞清楚里面的一些指针 begin(), end(), rbegin(), rend()之间的区别与联系,以及它们分别都指向哪个元素.首先要明白的一点 ...

  5. [转] - SendMessage、PostMessage原理

    SendMessage.PostMessage原理 本文讲解SendMessage.PostMessage两个函数的实现原理,分为三个步骤进行讲解,分别适合初级.中级.高级程序员进行理解,三个步骤分别 ...

  6. WPF DataGrid的分页实现

    原理:其实分页功能的实现大家都清楚,无非就是把一个记录集通过运算来刷选里面对应页码的记录. 接来下我们再次添加新的代码 <Grid> <DataGrid  Name="da ...

  7. Excel 2003 中如何用VBA 代码访问单元格里的值及操作单元格 - 唐诗宋词的专栏 - 博客频道 - CSDN.NET

    在Excel 中编写VBA 代码,最常做的事可能就是操作表单中单元格里的数据. 我这里总结一下如何从VBA 代码中操作单元格的数据. 在VBA 代码中操作单元格需要用到Range 对象,Range 是 ...

  8. html5+css3

    1,文件声明,<!Doctype>,不再有严格模式和混杂模式 2语意的标签 1,header 头 section中 nav导航(中上) aside侧边栏(中左) article内容(中右) ...

  9. Visual Studio 常用插件

    一.IndentGuide 缩进线插件:每个缩进块首尾添加虚线,使代码看着整洁. 其他插件:继续推荐几款VisualStudio的插件 二.CodeRush code rush 是微软推出的一款VS2 ...

  10. 使用HIBERNATE的SQL查询并将结果集自动转换成POJO

    在某些场合下,我们可能想使用HIBERNATE的框架提供的SQL查询接口,但是,由于实体没有做映射,HIBERNATE不能把结果集转换成你想要的List<POJO>,本文讨论如何在这种情况 ...