Cakephp 响应无法读取 UTF-8 文件名

Cakephp response cannot read UTF-8 file name

我想在登录检查后下载文件,所以在我的控制器中写了一个函数,比如

// Function to check login and download News PDF file
public function download(){

    if($this->Auth->user()){ 
        // Get the news file path from newsId 
        $pNewsObj  = ClassRegistry::init('PublicNews');
        $news = $pNewsObj->findById($newsId);

        $filePath = ROOT.DS.APP_DIR.DS.'webroot/upload_news'.DS.$news['PublicNews']['reference'];
        // Check if file exists
        if(!file_exists($filePath)){
            return $this->redirect('/404/index.php');
        }
        $this->response->charset('UTF-8');
        //$this->response->type('pdf');
        $this->response->file('webroot/upload_news'.DS.$news['PublicNews']['reference'],  array('download' => true, 'name' => $news['PublicNews']['reference']));
        //$this->response->download($news['PublicNews']['reference']);
        return $this->response;
    }else{
        return $this->redirect(array('controller'=> 'users', 'action' => 'login'));
    }
} 

现在,一切正常。

问题:当文件名是 UTF-8 格式时,例如。テステ.pdf(日语中的 Test.pdf)cakephp 抛出这样的错误。

对于英文文件名,它工作得很好,但我的客户希望文件名应该与上传的文件名相同,所以我无法将文件名更改为英文。

如果你想知道字符编码,你可以使用mb_detect_encoding()函数,如果输入文本有足够的长度来检测编码。 但我猜你的客户会上传 SJIS 文件。因为大多数日本人都在使用 SJIS,因为 Windows 已将 SJIS 用于日语。

我在本地环境中确认了您的代码。由于 cake 的 File class 似乎无法正确处理 SJIS,因此您不能使用 Response::file()。所以我写了替代代码。

public function download(){

    if($this->Auth->user()){ 
        // Get the news file path from newsId 
        $pNewsObj  = ClassRegistry::init('PublicNews');
        $news = $pNewsObj->findById($newsId);

        if (!$news) {
            throw new NotFoundException();
        }

        $fileName = mb_convert_encoding($news['PublicNews']['reference'], 'SJIS-win', 'UTF8');

        // Directory traversal protection
        if (strpos($fileName, '..') !== false) {
            throw new ForbiddenException();
        }

        $filePath = WWW_ROOT . 'upload_news' . DS . $fileName;
        if (!is_readable($filePath)) {
            throw new NotFoundException();
        }

        if (function_exists('mime_content_type')) {
            $type = mime_content_type($filePath);
            $this->response->type( $type );
        } else {
            // TODO: If Finfo extension is not loaded, you need to detect content type here;
        }

        $this->response->download( $fileName );
        $this->response->body( file_get_contents($filePath) );

        return $this->response;
    }else{
        return $this->redirect(array('controller'=> 'users', 'action' => 'login'));
    }
} 

但是,我建议您先将 SJIS 转换为 UTF8,然后再将其保存到数据库和磁盘中。没有足够的知识就很难处理 SJIS 字符。因为 SJIS 字符可能在第二个字节中包含 ascii 字符。尤其是反斜杠 (\) 是最危险的。例如,表 (955C) 包含一个反斜杠(5C = 反斜杠)。请注意,我不是在谈论罕见的情况。表在日语中表示 table 或外观。十还包含一个反斜杠,在日语中表示 10。能也包含一个反斜杠,表示技能。

与 UTF-8 字节序列不同,如果您处理 SJIS 字符,几乎所有字符串函数都无法正常工作。 explode() 会破坏 SJIS 字节序列。 strpos() 会 return 错误的结果。 您的客户端是直接使用 FTP 还是 SCP 连接到您的服务器?如果没有,最好在保存之前将 SJIS 转换为 UTF-8,并在 return 之前将 UTF-8 重新转换为 SJIS 给您的客户端。

如果您愿意,可以在上传文件之前更改文件名,这样在下载时就不会发生此错误。


    public function change_file_name($fileName= '') {
        $ext            =   pathinfo($fileName, PATHINFO_EXTENSION);
        $fileName       =   'file_'.time().".".$ext;
        $exFileName     =   strtolower(substr($fileName,strrpos($fileName,".") + 1));
        $sampleFileName =   str_replace('.'.$exFileName,'', $fileName);
        $name           =   Sanitize::paranoid($sampleFileName,array('_'));
        $fileRename     =   $name.'.'.$exFileName;
        return $fileRename;
    }

上传文件前调用此函数


    $return_file_name   =     $this->change_file_name($file_name);
    if($this->moveUploadedFile($tmp_name,WEBSITE_PROFILE_ROOT_PATH.$return_file_name)){
        $saveData['profile_image']          =   $return_file_name;
    }

我知道这不是你的正确答案case.For你可以做一个这样的函数,它将从数据库中获取数据并自动重命名所有保存文件并在你的数据库中更新它

关于您的客户规格的更多信息会很有帮助,但 Tom Scott 发现 base64 是使 Unicode 字符在 PHP 中正确工作的最简单方法。

根据在存储中保存文件名的重要性,解决方案可能是在上传文件时以 base64 对文件名进行编码,并在下载时反转编码。然后您可以知道您正在处理 ASCII,这应该更有可能正确工作。

您可能需要将 / 个字符替换为 %2F 才能使其正常工作。

希望这对您有所帮助,
伊萨·钱子