I want to download the file after login check so wrote a function in my controller like
// 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')); } } Now, everything works fine as required.
PROBLEM : when the file name is in UTF-8 eg. テスト.pdf (its Test.pdf in japanese) cakephp throws error like this.
For English filename it works perfectly fine but my client wants the filename should be the same as uploaded, so I can't change the filename to English.
3 Answers
Answers 1
If you want to know character encoding, you can use mb_detect_encoding() function if input text has enough length to detect encoding. But I am guessing your client would upload SJIS file. Because most Japanese people are using SJIS, as Windows has adopted SJIS for Japanese language.
I confirmed your code in my local environment. As cake's File class seems to be not able to handle SJIS correctly, you cannot use Response::file(). So I wrote alternative code.
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')); } } However, I recommend you to convert SJIS to UTF8 before save it into your database and your disk. It is difficult to handle SJIS characters without enough knowledge about it. Because SJIS characters may contain ascii characters in the second byte. Especially backslash (\) is most dangerous. For example, 表 (955C) contains a backslash (5C = backslash). Note that I am not talking about rare cases. 表 means table or appearance in Japanese. 十 also contains a backslash and it means 10 in Japanese. 能 also contains a backslash and it means skill.
Unlike UTF-8 byte sequence, if you handle SJIS characters, almost all string functions don't work correctly. explode() would break SJIS byte sequence. strpos() would return wrong result. Does your client connect to your server by using FTP or SCP directly? If not, it would be better to convert SJIS to UTF-8 before save, and re-convert UTF-8 to SJIS before return to your client.
Answers 2
If you like you can change the file name before uploading the file so at time of downloading this error will not happen.
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; } Call this function before uploading the file
$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; } I know this is not proper answer for your case.For this you can make a function like this which will fetch data from database and automatic rename all your save file and update it in your database
Answers 3
Some more information about your client's specifications would help greatly, but Tom Scott found base64 to be the simplest method of making Unicode characters work correctly in PHP.
Depending on how crucial the preservation of filenames in storage is, a solution could be to encode the filenames in base64 when files are uploaded, and reverse the encoding on download. You can then know that you are dealing with ASCII, which should be much more likely to work correctly.
You may need to replace / characters with %2F to make it work.
Hope this helps,
Issa Chanzi
0 comments:
Post a Comment