【发布时间】:2011-10-22 02:18:02
【问题描述】:
我正在开发一个简单的php上传脚本,用户只能上传ZIP和RAR文件。
我应该使用哪些 MIME 类型来检查 $_FILES[x][type]? (请提供完整列表)
【问题讨论】:
-
我想单独允许所有压缩文件(rar,zip,tar.gz,jar 等),程序是什么?
标签: php zip mime-types rar
我正在开发一个简单的php上传脚本,用户只能上传ZIP和RAR文件。
我应该使用哪些 MIME 类型来检查 $_FILES[x][type]? (请提供完整列表)
【问题讨论】:
标签: php zip mime-types rar
freedompeace、Kiyarash 和 Sam Vloeberghs 的回答:
.rar application/x-rar-compressed, application/octet-stream
.zip application/zip, application/octet-stream, application/x-zip-compressed, multipart/x-zip
我也会检查文件名。以下是检查文件是 RAR 还是 ZIP 文件的方法。我通过创建一个快速命令行应用程序对其进行了测试。
<?php
if (isRarOrZip($argv[1])) {
echo 'It is probably a RAR or ZIP file.';
} else {
echo 'It is probably not a RAR or ZIP file.';
}
function isRarOrZip($file) {
// get the first 7 bytes
$bytes = file_get_contents($file, FALSE, NULL, 0, 7);
$ext = strtolower(substr($file, - 4));
// RAR magic number: Rar!\x1A\x07\x00
// http://en.wikipedia.org/wiki/RAR
if ($ext == '.rar' and bin2hex($bytes) == '526172211a0700') {
return TRUE;
}
// ZIP magic number: none, though PK\003\004, PK\005\006 (empty archive),
// or PK\007\008 (spanned archive) are common.
// http://en.wikipedia.org/wiki/ZIP_(file_format)
if ($ext == '.zip' and substr($bytes, 0, 2) == 'PK') {
return TRUE;
}
return FALSE;
}
请注意,它仍然不能 100% 确定,但可能已经足够了。
$ rar.exe l somefile.zip
somefile.zip is not RAR archive
但即使是 WinRAR 也将非 RAR 文件检测为 SFX 存档:
$ rar.exe l somefile.srr
SFX Volume somefile.srr
【讨论】:
application/x-zip-compressed
zip 或rar 文件。根据WC3 specifications,这将被解释为:“我更喜欢application/zip|application/x-rar-compressed 内容类型,但如果你不能提供这个application/octet-stream(文件流)也可以”。我>
multipart/x-zip 到底怎么可能是有效的?它不是多部分的。 SitePoint 列表包含许多不准确的 MIME 类型,而且还远未完成。官方IANA Media Types registry 尚未(也可能永远不会)100% 完成。
可以在The Internet Assigned Numbers Authority (IANA) 找到官方的 mime 类型列表。根据他们的列表,zip 的 Content-Type 标头是 application/zip。
rar 文件的媒体类型未在 IANA 正式注册,但非官方常用的 mime-type 值为application/x-rar-compressed。
application/octet-stream 的意思是:“我给你发了一个文件流,但没有指定这个流的内容”(所以确实可以是zip 或@ 987654331@ 文件)。服务器应该检测流的实际内容是什么。
注意:对于上传,依赖Content-Type 标头中设置的mime 类型是不安全的。标头在客户端上设置,可以设置为任何随机值。相反,您可以使用 the php file info 函数来检测服务器上的文件 mime 类型。
如果你想下载一个zip 文件而不是别的,你应该只设置一个Accept 标头值。如果服务器无法满足您在 Accept 标头请求的 mime 类型中的要求,任何附加值设置将用作后备。
根据WC3 specifications这个:
application/zip, application/octet-stream
将被解释为:“我更喜欢application/zip mime 类型,但如果你不能提供这个application/octet-stream(文件流)也可以”。
所以只有一个:
application/zip
将向您保证zip 文件(或406 - Not Acceptable 响应,以防服务器无法满足您的请求)。
【讨论】:
您不应该相信$_FILES['upfile']['mime'],请自行检查 MIME 类型。为此,您可以使用fileinfo extension,自 PHP 5.3.0 起默认启用。
$fileInfo = new finfo(FILEINFO_MIME_TYPE);
$fileMime = $fileInfo->file($_FILES['upfile']['tmp_name']);
$validMimes = array(
'zip' => 'application/zip',
'rar' => 'application/x-rar',
);
$fileExt = array_search($fileMime, $validMimes, true);
if($fileExt != 'zip' && $fileExt != 'rar')
throw new RuntimeException('Invalid file format.');
注意:不要忘记在 php.ini 中启用扩展并重新启动服务器:
extension=php_fileinfo.dll
【讨论】:
我看到很多关于 zip 和 rar 媒体类型 application/zip 和 application/x-rar-compressed 的答案报告。
虽然前一个匹配是正确的,但对于后一个 IANA 报告,https://www.iana.org/assignments/media-types/application/vnd.rar rar 的 application/x-rar-compressed 是一个已弃用的别名,而 application/vnd.rar 是官方别名。
因此,2020 年 IANA 的正确媒体类型是:
zip: application/zip
rar: application/vnd.rar
【讨论】:
在a linked question 中,有一些Objective-C 代码可以获取文件URL 的mime 类型。我已经基于该 Objective-C 代码创建了一个 Swift 扩展来获取 mime 类型:
import Foundation
import MobileCoreServices
extension URL {
var mimeType: String? {
guard self.pathExtension.count != 0 else {
return nil
}
let pathExtension = self.pathExtension as CFString
if let preferredIdentifier = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension, nil) {
guard let mimeType = UTTypeCopyPreferredTagWithClass(preferredIdentifier.takeRetainedValue(), kUTTagClassMIMEType) else {
return nil
}
return mimeType.takeRetainedValue() as String
}
return nil
}
}
【讨论】:
由于扩展名可能包含或多或少三个字符,以下将测试扩展名,无论其长度如何。
试试这个:
$allowedExtensions = array( 'mkv', 'mp3', 'flac' );
$temp = explode(".", $_FILES[$file]["name"]);
$extension = strtolower(end($temp));
if( in_array( $extension, $allowedExtensions ) ) { ///
检查最后一个'.'之后的所有字符
【讨论】: