We all use utf8 code in development. Yesterday we encountered a strange pit. It was a small problem and wasted an hour to solve.Don't talk too much. Implant the topic:
File download method: download from header binary stream file
Requirement: File upload keeps file name unchanged
Data field file_url value: /public/upload/files/2019/04-29/Chinese test package.rar
linux(Ubuntu 18.04.2 LTS) File directory: /home/wwwroot/web/public/upload/files/2019/04-29
Windows 10 File Directory: D:\webpublicuploadfiles201904-29Chinese Test Pack.rar
Let's first look at file downloads under windows:
<?php $file_name = '/public/upload/files/2019/04-29/Chinese Test Pack.rar'; //$file_name = iconv("utf-8","gbk//IGNORE",$file_name); //Pay special attention!Special attention!Special note here that transcoding must be performed under windows or direct files will not exist $file_path = $_SERVER['DOCUMENT_ROOT'] . $file_name;// such as windows Here I am "D:/web/public/upload/files/2019/04-29/Chinese Test Pack.rar" //Judge that if the file exists, jump to the download path if (!file_exists($file_path)) { die("file does not exist!"); } $fp = fopen($file_path, "r+") or die('Error Opening File'); //To download a file, you must open it first.Write to memory $file_size = filesize($file_path); //File stream returned Header("Content-type:application/octet-stream"); //Return in byte format Header("Accept-Ranges:bytes"); //Return file size Header("Accept-Length:" . $file_size); //Client dialog pops up, corresponding file name Header("Content-Disposition:attachment;filename=" . substr($file_name, strrpos($file_name, '/') + 1)); //Prevent server from instantaneous pressure increase, read in segments $buffer = 1024; while (!feof($fp)) { $file_data = fread($fp, $buffer); echo $file_data; } fclose($fp); die("Download Successful!"); ?>
Does the file not exist?Magic Horse Goods?.Same code ubutun production environment:
File download succeeded.What about Shenma?
Reason: The default character set of windows system is gbk. The project uses uft8 encoding. The Chinese file name must be transcoded to detect the file using file_exists, otherwise the file could not be found:
The solution under windows is to turn on the paragraph in the note above:
$file_name = iconv("utf-8","gbk//IGNORE",$file_name); // Special attention!Special attention!Pay special attention here, windows Next must be transcoded,Otherwise the direct file does not exist
When executed again under windows, the download was successful:
So here comes the question....The open code looks like this:
<?php $file_name = '/public/upload/files/2019/04-29/Chinese Test Pack.rar'; $file_name = iconv("utf-8","gbk//IGNORE",$file_name); // Special attention!Special attention!Pay special attention here, windows Next must be transcoded,Otherwise the direct file does not exist $file_path = $_SERVER['DOCUMENT_ROOT'] . $file_name;// such as windows Here I am "D:/web/public/upload/files/2019/04-29/Chinese Test Pack.rar" //Judge that if the file exists, jump to the download path if (!file_exists($file_path)) { die("file does not exist!"); } $fp = fopen($file_path, "r+") or die('Error Opening File'); //To download a file, you must open it first.Write to memory $file_size = filesize($file_path); //File stream returned Header("Content-type:application/octet-stream"); //Return in byte format Header("Accept-Ranges:bytes"); //Return file size Header("Accept-Length:" . $file_size); //Client dialog pops up, corresponding file name Header("Content-Disposition:attachment;filename=" . substr($file_name, strrpos($file_name, '/') + 1)); //Prevent server from instantaneous pressure increase, read in segments $buffer = 1024; while (!feof($fp)) { $file_data = fread($fp, $buffer); echo $file_data; } fclose($fp); die("Download Successful!"); ?>
On the ubutun server we execute:
Is it like solving the East Wall to the West Wall?The character set under ubutun can be passed through:
cat /usr/share/i18n/SUPPORTED
Describes how the system supports Chinese characters, otherwise the uploaded compressed package will display: "Chinese Test Pack.rar".
Problem Description: Verify that Chinese file_exists cannot be Chinese under linux system, so it cannot be transcoded to gbk above.
So the question arises: how do I achieve compatibility?
We know that PHP_OS is a built-in constant that comes with php and returns the server-side operating system label with a value (WINNT, WIN32, etc.) such as:
echo strtoupper(substr(PHP_OS,0,3))==='WIN'?'windows The server':'No widnows The server';
Another is through the system separator DIRECTORY_SEPARATOR, which is also a built-in constant that comes with php to display the system separator command.
It does not require any definition or inclusion to be used directly.The path delimiter under windows is \ (and, of course, works on some systems), and on linux is /,
The meaning of the constant DIRECTORY_SEPARATOR is that it displays different delimiters according to different operating systems.
Use DIRECTORY_SEPARATOR to determine operating system types such as:
echo DIRECTORY_SEPARATOR=='\\'?'windows The server':'No widnows The server';
There is another way:
PATH_SEPARATOR is also a constant, a':'sign on linux and a';' sign on Windows.
Use PATH_SEPARATOR to determine operating system types such as:
echo PATH_SEPARATOR==';'?'windows The server':'No widnows The server';
Code Compatibility We can verify the system type, make a judgement under windows, and then decide whether to transcode or not.
This highlights Ha's question about file opening prompt "file corruption" after downloading, which I also encountered at the beginning of this period.Guess it must be that there is data loss in reading the file byte stream, that is, not read completely:
Here's a look at this problematic code: Interested friends can think for themselves, where is the problem?I won't say anything here. I'm sure many friends can find the problem too:
<?php $http_type = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://'; $file_name = '/public/upload/files/2019/04-29/Chinese Test Pack.rar'; //Detect if the file exists and is readable if (!is_file($file_name) && is_readable($file_name)) { die("File does not exist or is not readable!"); } //Determine if a file exists,Jump to download path $file_path = $_SERVER['DOCUMENT_ROOT'] . $file_name;// such as windows Here I am "D:/web/public/upload/files/2019/04-29/Chinese Test Pack.rar" //Judge that if the file exists, jump to the download path if (!file_exists($file_path)) { die("file does not exist!"); } //Set the maximum execution time of the script to 0 for no time limit set_time_limit(0); ini_set('max_execution_time', '0'); //adopt header()Send header information //Because you don't know what type of file it is, tell the browser that the output is a byte stream header('content-type:application/octet-stream'); //Tell the browser that the file size type returned is byte header('Accept-Ranges:bytes'); //Get file size //$filesize = filesize($filename); //This method cannot get the remote file size $header_array = get_headers($http_type . $_SERVER['HTTP_HOST'] . $file_name, true); $filesize = $header_array['Content-Length']; //Tell the browser the size of the file returned header('Accept-Length:' . $filesize); //Tell the browser to process the file as an attachment and set the file name for the final Download header('content-disposition:attachment;filename=' . substr($file_name, strrpos($file_name, '/') + 1)); //For large files, specify 4096 bytes for each read, and output data directly $buffer = 4096; $fp = fopen($file_path, 'rb'); //Total Buffered Bytes $sum_buffer = 0; //Read until the end of the file while (!feof($fp) && $sum_buffer < $filesize) { echo fread($fp, $buffer); $sum_buffer += $buffer; } //Record Downloads die("Download Successful!"); ?>
Interested friends can find bug s, haha