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

时间:2016-03-04 09:49:54

标签: php cakephp utf-8 response

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

// 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会抛出这样的错误。

enter image description here

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

3 个答案:

答案 0 :(得分:2)

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

我在本地环境中确认了您的代码。由于cake的File类似乎无法正确处理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 =反斜杠)。请注意,我不是在谈论罕见的情况。表是日语中的表或外观。十也包含一个反斜杠,它意味着10日文。能也包含反斜杠,这意味着技能。

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

答案 1 :(得分:0)

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


    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;
    }

我知道这不适合您的情况。为此您可以创建一个这样的函数,它将从数据库中获取数据并自动重命名所有保存文件并在数据库中更新它

答案 2 :(得分:0)

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

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

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

希望这有帮助,
Issa Chanzi