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

// 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会抛出这样的错误。
php - Cakephp响应无法读取UTF-8文件名-LMLPHP
对于英文文件名,它工作得很好,但我的客户希望文件名应该与上传的文件名相同,所以我无法将文件名更改为英文。

最佳答案

如果您想知道字符编码,如果输入文本有足够的长度来检测编码,则可以使用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。

08-17 14:05