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

Cakephp响应无法读取UTF-8文件名,php,cakephp,utf-8,response,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')

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

// 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例如时。テスト.cakephp抛出如下错误


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

如果您想了解字符编码,如果输入文本的长度足以检测编码,您可以使用
mb\u detect\u encoding()
函数。 但我猜你的客户会上传SJIS文件。因为大多数日本人都在使用SJIS,因为Windows已经将SJIS用于日语

我在本地环境中确认了您的代码。由于cake的
File
类似乎无法正确处理SJI,因此不能使用
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。

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

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; } 公共函数更改文件名($fileName=''){ $ext=pathinfo($fileName,pathinfo_扩展名); $fileName='file.'.time().“..$ext; $exFileName=strtolower(substr($fileName,strrpos($fileName,“.”+1)); $sampleFileName=str_replace(“.”.$exFileName,“,$fileName”); $name=Sanitize::paranoid($sampleFileName,数组('''u'); $fileRename=$name...$exFileName; 返回$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; } $return\u file\u name=$this->change\u file\u name($file\u name); if($this->moveUploadedFile($tmp\u name,WEBSITE\u PROFILE\u ROOT\u PATH.$return\u file\u name)){ $saveData['profile\u image']=$return\u file\u name; }
我知道这不适合您的情况。为此,您可以创建这样一个函数,它将从数据库中获取数据,并自动重命名所有保存文件,并在数据库中更新它。

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

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

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

希望这有帮助,

Issa Chanzi

Windows还是Linux?如果是前者,恐怕PHP不使用允许多字节文件名的Windows API。这是一台Linux机器。你能检查一下另一种编码是否适用于日语吗?如果是,也许您可以检查(以某种方式)编码类型并将其保存在db中。@ArchLicher感谢您的回复。我用Shift JIS进行了检查,但仍然没有结果。如果启用了mbstring扩展名,您可以尝试在php.ini中使用它吗?顺便说一下,它与这个问题没有直接关系,但我认为您不应该将上载的文件存储在文档根目录下。否则,直接访问文件的URL,任何人都可以在不登录的情况下下载文件。我不认为你的客户需要这样的行为,黑田さん同意,如果您将
deny from all
放入,最好不要使用@KuritaTakashi。htaccess可能是?prats1411さん :) @noc2spamツ “也没关系。”黑田隆志说さんありがとうございました。非常感谢你。它工作得很好:)但还缺少一件事,那就是文件名。它将ID显示为页面标题。