调试文件没有被原子更新(可能是PDF.js浏览器问题)


Debug files not being updated atomically (possible PDF.js browser issue)

我正在使用webkitmltopdf生成PDF&希望覆盖任何现有文件。

我不确定这是否保证了原子更新,或者我们是否有一天会使用不同的PDF工具,所以我已经将其封装在一些使用临时文件的PHP代码中。创建临时文件后,我使用PHP的rename()函数来覆盖实际文件。

我已经确保临时文件&输出文件存在于同一个分区上,但是当我运行脚本时,如果我在PDF被覆盖的确切时刻请求PDF,我有时会从浏览器收到"PDF文件无法显示"类型的消息。

我如何尝试调试它?我在Apache错误日志中没有看到任何错误。我在访问日志中看到"200"answers"206"请求。我不确定我在内容长度上寻找什么,也不确定pdf.js是如何与服务器协同工作的。

代码如下:

$output = sprintf(__DIR__."/pdfs/%s.pdf", $id);
$tmpOutput = $output . '.tmp';
$cmd = 'wkhtmltopdf '. escapeshellarg($url) . ' ' . escapeshellarg($tmpOutput);
exec($cmd);
chmod($tmpOutput, 0777);
rename($tmpOutput, $output);
chmod($output, 0777);

值得一提的是,我正在使用chmod来解决我在一个由root启动的gearman工作人员中运行这个问题。如果这是一个权限问题,我会在Apache错误日志中看到一个错误,但没有——我也会看到403状态代码或类似的代码,但我在日志中看到的只是200或206。

大多数PDF阅读器以块的形式从网上读取PDF文件,这意味着在几个HTTP请求中使用Range标头(指定它想要的文件的字节范围,例如1000-5000,因此字节数为4000)。Web服务器使用HTTP 206 Partial Content响应代码进行回复。如果您在这些部分请求之间更改PDF文件,PDF阅读器将收到损坏的文件(部分来自旧文件,部分来自新文件)。

HTTP协议应该阻止它——在第一次请求时,PDF阅读器还应该接收ETAg标头,它是唯一的,如果文件发生更改,就会发生更改。在随后的请求中,PDF阅读器应发送If-Match标头,以便Web服务器可以通知它文件是否仍然相同。但有时这不起作用。您可以使用以下命令禁用apache配置(或.htaccess文件)中的Range请求:

<Files *.pdf>
  Header set Accept-Ranges none
</Files>

还要确保您的临时文件始终是唯一的,这样就不会有两个PHP进程同时写入同一个临时文件名。