为了账号安全,请及时绑定邮箱和手机立即绑定

Laravel:表单文件上传失败 - 检测到错误类型

Laravel:表单文件上传失败 - 检测到错误类型

PHP
饮歌长啸 2021-11-05 18:39:00
我最近使用了一个上传简历的系统,我收到了异常高的拒绝数量,大约 15%。当我查看日志时,它显示了以下结果:客户端报告的扩展名是docx(基于$file->getClientOriginalExtension())客户端报告的 MIME 类型是application/vnd.openxmlformats-officedocument.wordprocessingml.document(基于$file->getClientMimeType())Laravel/PHP 检测到文件是.bin(基于$file->extension())换句话说,客户端说它正在上传一个 docx 文件,但服务器说它收到了一个 bin。起初我以为这只是腐败,但它经常发生。到目前为止,我只在日志中捕获了一些,但我注意到每次 Safari 都是浏览器。Safari 是否存在任何系统性问题?如果是这样,我该如何绕过它们?还有什么可能导致问题?我正在使用 Vue 触发表单提交,但我看不出这有什么不同。这是Vue代码:document.getElementById("new-application").submit();
查看完整描述

2 回答

?
米脂

TA贡献1836条经验 获得超3个赞

问题在于 PHP 使用file来自操作系统(在本例中为 Ubuntu)的命令根据文件内容猜测扩展名。它弄错了。升级 Ubuntu 可能会有所帮助。


查看完整回答
反对 回复 2021-11-05
?
摇曳的蔷薇

TA贡献1793条经验 获得超6个赞

docx是 Microsoft Word Open XML 格式文档,它是一种基于 XML 的文件,所有内容都存储为单独的文件,并最终压缩为一个 ZIP 压缩文件。它看起来像一个文件容器。


并且我们不应该将文件扩展名与 mimetype 相同。据了解,具有docx文件扩展名的正常(带有 docx 的 mp4 文件不应视为正常)文件可以具有这些 mimetypes 之一。


'application/vnd.openxmlformats-officedocument.wordprocessingml.document'

'application/zip'

'application/CDFV2'

在您的代码中,


$file->getClientOriginalExtension() extract extension from the original file name that was uploaded which should not be considered as a safe value.


$file->getClientMimeType() extract the mime type from client request which should not be considered as a safe value.


Both these two functin implemented in ./vendor/symfony/http-foundation/File/UploadedFile.php


Then take a look at last function in your code,


$file->extension() use some method to guess (which maybe not accurate) the file extension. In sequence php's finfo (manual) which base on the file content, the system file. As the below source code it's guess, so it's not accurate all the time. Refer to file command apparently returning wrong MIME type.


Here you should get it. If you want to go further, see below source code of the function extension.


Source Code

extension call guessExtension to guess the file extension, and guessExtension use the mimetype returned by guessMiMeType


// FileHelpers.php

public function extension()

{

    return $this->guessExtension();

}

// File.php

public function guessExtension()

{

    return MimeTypes::getDefault()->getExtensions($this->getMimeType())[0] ?? null;

}

...

public function getMimeType()

{

    return MimeTypes::getDefault()->guessMimeType($this->getPathname());

}


guessMiMeType use two guesser to guess the mimetype. php's finfo and system's file, and finfo take high priority than file.


//MimeTypes.php

public function __construct(array $map = [])

{

    foreach ($map as $mimeType => $extensions) {

        $this->extensions[$mimeType] = $extensions;


        foreach ($extensions as $extension) {

            $this->mimeTypes[$extension] = $mimeType;

        }

    }

    $this->registerGuesser(new FileBinaryMimeTypeGuesser());

    $this->registerGuesser(new FileinfoMimeTypeGuesser());

}

...

/**

* Registers a MIME type guesser.

*

* The last registered guesser has precedence over the other ones.

*/

public function registerGuesser(MimeTypeGuesserInterface $guesser)

{

    array_unshift($this->guessers, $guesser);

}

...

public function guessMimeType(string $path): ?string

{

    foreach ($this->guessers as $guesser) {

        if (!$guesser->isGuesserSupported()) {

            continue;

        }


        if (null !== $mimeType = $guesser->guessMimeType($path)) {

            return $mimeType;

        }

    }


    if (!$this->isGuesserSupported()) {

        throw new LogicException('Unable to guess the MIME type as no guessers are available (have you enable the php_fileinfo extension?).');

    }


    return null;

}

//FileinfoMimeTypeGuesser.php

public function guessMimeType(string $path): ?string

{

    if (!is_file($path) || !is_readable($path)) {

        throw new InvalidArgumentException(sprintf('The "%s" file does not exist or is not readable.', $path));

    }


    if (!$this->isGuesserSupported()) {

        throw new LogicException(sprintf('The "%s" guesser is not supported.', __CLASS__));

    }


    if (false === $finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) {

        return null;

    }


    return $finfo->file($path);

}

//FileBianryMimeTypeGuesser.php

public function __construct(string $cmd = 'file -b --mime %s 2>/dev/null')

{

    $this->cmd = $cmd;

}

    public function guessMimeType(string $path): ?string

{

    if (!is_file($path) || !is_readable($path)) {

        throw new InvalidArgumentException(sprintf('The "%s" file does not exist or is not readable.', $path));

    }


    if (!$this->isGuesserSupported()) {

        throw new LogicException(sprintf('The "%s" guesser is not supported.', __CLASS__));

    }


    ob_start();


    // need to use --mime instead of -i. see #6641

    passthru(sprintf($this->cmd, escapeshellarg($path)), $return);

    if ($return > 0) {

        ob_end_clean();


        return null;

    }


    $type = trim(ob_get_clean());


    if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\.]+)#i', $type, $match)) {

        // it's not a type, but an error message

        return null;

    }


    return $match[1];

}


查看完整回答
反对 回复 2021-11-05
  • 2 回答
  • 0 关注
  • 688 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信