【问题标题】:PHP - Filenames in HTTP-headers: Problem with whitespacesPHP - HTTP 标头中的文件名:空格问题
【发布时间】:2011-08-31 10:25:29
【问题描述】:

使用 CakePHP 和 Java Web Start 我正在控制器中生成必要的 .jnlp 文件,其中我将文件名设置为标题字段。只要我不尝试在文件名中使用特殊字符,它就可以正常工作。但是,我想启用主要操作系统上可能的每个字符作为文件名。所以我尝试做的是通过空字符串替换它们来删除所有无效字符。但是文件名中应该允许使用空格似乎存在问题。

代码如下:

$panel_id = 1
$panelname = 'w h i t e s p a c e s';
$filename = sprintf('"Project_%d_%s.jnlp"', $panel_id, $panelname);
$invalid_chars = array('<', '>', '?', '"', ':', '|', '\\', '/', '*', '&');
$filename = str_replace($invalid_filenamechars, '', $filename);
$this->header('Content-Disposition: attachment; filename=' . $filename);

当我这样做时,标题中的结果文件名是“Project_1_w h i t e s p a c e”,而 Windows 7 想要将文件保存为“Project_1_w”。所以看来我的操作系统不接受文件名中未转义的空格?如果不是因为以下原因,我会对这个解释感到满意:我离开了第 4 行和第 5 行,以便代码看起来

$panel_id = 1
$panelname = 'w h i t e s p a c e s';
$filename = sprintf('"Project_%d_%s.jnlp"', $panel_id, $panelname);
$this->header('Content-Disposition: attachment; filename=' . $filename);

现在 Windows 愿意保存带有所有空格的文件,但我仍然不明白为什么。如果我使用wireshark查看标题中的文件名,两者都是相同的。如果 sprintf 行被 $filename = 'w h i t e s p a c e' 甚至 $filename = $panelname 替换,它会像第一个代码 n-p 中一样剪切文件名。但是我可以用 dottet-string-concat 语法替换 sprintf 并且它可以工作。

谁能告诉我,我忽略了什么?

【问题讨论】:

  • 严格来说,您应该在标题中用双引号将文件名括起来:Content-Disposition: attachment; filename="My File Name.ext"。如果您这样做,是否可以解决问题?

标签: php windows http filenames


【解决方案1】:

区别在于双引号。使用第一个代码,您最终会得到:

Content-Disposition: attachment; filename=Project_1_w h i t e s p a c e s.jnlp

你会得到第二个代码:

Content-Disposition: attachment; filename="Project_1_w h i t e s p a c e s.jnlp"

你可能想要的是这样的:

$panel_id = 1;
$panelname = 'w h i t e s p a c e s';
$filename = sprintf('"Project_%d_%s.jnlp"', $panel_id, $panelname);
$invalid_chars = array('<', '>', '?', '"', ':', '|', '\\', '/', '*', '&');
$filename = str_replace($invalid_filenamechars, '', $filename);
$this->header('Content-Disposition: attachment; filename="'.$filename.'"');

这会去除 $filename 中的所有双引号,但会确保 $filename 始终被双引号括起来。

【讨论】:

  • 好吧,现在我觉得自己很愚蠢。通过查看标题应该已经看到了。谢谢!
  • 是的!诀窍是在整个标题字符串周围使用单引号,在文件名周围使用双引号。如果文件不完全一样,某些浏览器将无法下载/保存文件(mac 上的 firefox)。
【解决方案2】:

RFC2616, which is the HTTP/1.1 spec, 这么说:

Content-Disposition 响应标头字段已被提议作为 意味着如果用户使用原始服务器建议默认文件名 请求将内容保存到文件中。这种用法是派生的 来自 RFC 1806 中 Content-Disposition 的定义。

    content-disposition = "Content-Disposition" ":"
                          disposition-type *( ";" disposition-parm )
    disposition-type = "attachment" | disp-extension-token
    disposition-parm = filename-parm | disp-extension-parm
    filename-parm = "filename" "=" quoted-string
    disp-extension-token = token
    disp-extension-parm = token "=" ( token | quoted-string )

一个例子是 内容处置:附件;文件名="fname.ext"

因此,发送这个:

header('Content-Disposition: attachment; filename="' . $filename . '"');

符合第二种形式 (quoted-string) 并且应该按照您的预期进行 - 注意只发送 SPACE (ASCII dec 32 / hex 20) 作为空格,而不是其他一些花哨的空格字符。

【讨论】:

  • 仅供参考,RFC2616 (HTTP/1.1) 将文件名值部分定义为 quoted-string
  • @DaveRandom:谢谢,很高兴知道。 你正处于一个曲折的小标准的迷宫中,完全不同。
  • 确实是这样,因为同样的东西被定义了一千次,所以很难知道该怎么做。我想我可能已经阅读了 300 遍 RFC2616,它是我唯一真正阅读过的。有很多其他 RFC 说您可以做的事情,但 HTTP 不允许这样做,我很少相信我在其他任何地方读到的任何内容。如果您想要一些有趣(令人困惑的)阅读并且您有一天感到无聊,请尝试RFC959(FTP)并享受所有直接相互矛盾的陈述......
  • @DaveRandom:我最初查看 RFC2616 的标头,但没有找到(因为它在它自己的部分中,与所有其他标头完全不同)。将编辑。
  • 相关的 RFC 是 RFC 6266。另外请记住,如果您需要支持 ISO-8859-1 范围之外的字符,则需要做更多的工作。
猜你喜欢
  • 2013-11-18
  • 1970-01-01
  • 2015-12-12
  • 2019-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多