使用Laravel 4进行文件上传mime类型验证

4
当我上传一个格式正确的MP3文件时,Laravel 4告诉我它不是audio/mp3,而是application/octet-stream,这导致验证失败:
$validator = Validator::make(
    array('trackfile' => Input::file('trackfile')), 
    array('trackfile' => 'required|mimes:mp3')
);

if($validator->fails())
    return 'doesn\'t works because mime type is '.Input::file('trackfile')->getMimeType();
else
    return 'it works!';

为什么它不将文件上传为audio/mp3格式?(我已经在表单声明中添加了'files' => true

也许查看vendor\symfony\http-foundation\Symfony\Component\HttpFoundation\File\File.php可以帮助。我认为这就是getMimeType()所在的地方。 - chris342423
array('trackfile' => Input::file('trackfile')) 是用来做什么的? - The Alpha
@SheikhHeera将'trackfile'规则键绑定到'trackfile'文件输入。你有什么建议?即使我不确定这是否是问题的原因! - ryancey
从规则设置中删除此行 array('trackfile' => Input::file('trackfile') - The Alpha
我必须为验证器指定一个输入,不能只是删除这一行。此外,如果我只放置 Input::file('trackfile'),它会告诉我它期望一个数组而不是一个对象。 - ryancey
这并不是导致验证失败的原因,很可能是由于 mime_content_type 无法准确识别 mp3 文件。 - The Alpha
2个回答

12

编辑

内置验证器仍将某些mp3视为application/octet-stream,无论您使用哪个软件转换原始文件。

最后,我使用用户ShaneMimeReader类,它完全可行。不过我仍在想为什么检测正确的mime类型那么难。

我在Laravel中将其实现为一个验证器:

  • MimeReader.phps移动到app/libraries/MimeReader.php(注意扩展名)
  • 扩展Laravel的Validator(我将其放在了我的BaseController构造函数中)

Validator::extend('audio', function($attribute, $value, $parameters)
{
    $allowed = array('audio/mpeg', 'application/ogg', 'audio/wave', 'audio/aiff');
    $mime = new MimeReader($value->getRealPath());
    return in_array($mime->get_type(), $allowed);
});
  • 将您的错误消息添加到 app/lang/[whatever]/validation.php 中(在我的情况下,密钥是 audio
  • 我现在可以将音频用作规则! 例如:'file' => 'required | audio',其中 file 指的是 Input ::file('file')

  • Laravel认为 audio/mpeg 文件是 .mpga 而不是 .mp3 。 我在 MimeTypeExtensionGuesser.php (在Symfony库中)中进行了更正,以及被视为 .oga audio/ogg 。 也许音频mime类型取决于使用的软件编码器。

    另一种方法是绕过验证器并直接在控制器中使用 Input ::file('upload')-> getMimeType()检查mime类型,就像Sheikh Heera所说的那样。


    1
    +1,很高兴你分享了解决方案,尽量描述清楚,这样它将成为此类问题的参考。 - The Alpha
    @Shane的MimeReader域名已经失效了,还有其他地方可以找到吗? - Alwin Kesler

    6

    可能是因为 mp3 文件的 Mime 类型是 audio/mpeg,或者它无法识别 mime_content_type 并将 application/octet-stream 告知为通用 / 回退类型。您可以尝试使用以下规则(下面给出示例),而不是使用原有规则(未经测试/确认),因为根据 rfc3003mp3 的正确 Mime 类型是 audio/mpeg

    array('trackfile' => 'required|mimes:audio/mpeg,mp3')
    

    此外,您可以手动检查 mime 类型,例如:
    $file = Input::file('upload');
    if($file->getMimeType() == 'audio/mpeg') {
        // valid
    }
    else {
        // invalid
    }
    

    更新:

    特别针对mp3文件,您还可以使用类似于Php PECL扩展id3的库getID3,如果您使用它,您也将能够使用文件的ID3标签,并获取诸如标题艺术家专辑年份流派等信息。

    如果您要使用它,请从给定的链接(getid3)手动下载并将其放入app内的一个文件夹中,例如app/libs/,然后将从提取的源代码中获取的getid3文件夹放入libs文件夹中,然后在您的composer.json文件中,在autoload部分的classmap中添加一个条目,如下所示:

    // ...
    "app/tests/TestCase.php",
    "app/libs/getid3"
    

    然后在终端/命令提示符中运行composer dump-autoload,您就可以使用它了。要使用它,请尝试以下操作:
    $file = Input::file('upload'); // assumed name of the file input is upload
    $path = $path = $file->getRealPath();
    $id3 = new getID3();
    $tags = $id3->analyze($path);
    if(is_array($tags) && array_key_exists('audio', $tags)) {
        // valid
        dd($tag['tags']);
    }
    

    对于有效的 mp3 文件,您将获得以下数组:
    array (size=2)   'id3v1' => 
        array (size=6)
          'title' => 
            array (size=1)
              0 => string '46. Piya Basanti' (length=16)
          'artist' => 
            array (size=1)
              0 => string '(Freshmaza.com)' (length=15)
          'album' =>
          ...
    

    如果您已经安装或可以使用以下命令(从终端)安装PECL扩展:

    pear install pecl/id3
    

    那么您可以正常使用。
    $tags = id3_get_tag( "path/to/example.mp3" );
    print_r($tag);
    

    以上示例将输出类似以下内容:

    Array
    (
        [title] => DN-38416
        [artist] => Re:\Legion
        [album] => Reflections
        [year] => 2004
        [genre] => 19
    )
    

    请查阅Php手册中的相关内容。


    我解决了这个问题。在验证器中指定“audio/mpeg”没有起作用,我必须直接在 MIME 类型猜测文件中做出更改。虽然感谢您关于 ID3 标签的额外评论和耐心等待!我一定会进一步研究的。 - ryancey

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接