Я создал функцию для своего приложения Symfony на основе этого ответа: https://stackoverflow.com/a/13025363/1749653
Функция такова:
public function getForceDownloadResponse($file_path, $file_name){
$file_info = finfo_open(FILEINFO_MIME_TYPE);
$mine_type = finfo_file($file_info, $file_path.$file_name);
finfo_close($file_info);
$response = new Response();
$response->headers->set('Cache-Control', 'private');
$response->headers->set('Content-type', $mine_type);
$response->headers->set('Content-Disposition', 'attachment; filename="' . $file_name . '"');
$response->headers->set('Content-length', filesize($file_path.$file_name));
$response->sendHeaders();
$response->setContent(readfile($file_path.$file_name));
return $response;
}
И все это работало довольно хорошо … пока кто-то не попытался загрузить текстовый файл с ним.
По какой-то причине все текстовые файлы загружаются с неправильным именем файла. Все они следуют этой схеме:
Actual_File_Name-, прикрепление
так, например: new_text.txt-, attachment
так далее
После некоторого расследования я обнаружил только одну неисправность с текстовыми файлами в целом. Каким-то образом поля заголовка ответа дублируются.
Где двоичные файлы будут выводить что-то вроде этого:
Cache-Control:private
Connection:close
Content-Disposition:attachment; filename="Jellyfish.jpg"Content-Length:775702
Content-Type:image/jpeg
Date:Mon, 10 Nov 2014 09:14:41 GMT
Server:Apache/2.4.7 (Win32) OpenSSL/1.0.1e PHP/5.5.6
Любой текстовый файл будет выглядеть так:
Cache-Control:private
Cache-Control:private
Connection:Keep-Alive
Content-Disposition:attachment; filename="full.txt"Content-Disposition:attachment; filename="full.txt"Content-Length:15
Content-Type:text/plain; charset=UTF-8
Date:Mon, 10 Nov 2014 09:06:00 GMT
Keep-Alive:timeout=5, max=97
Server:Apache/2.4.7 (Win32) OpenSSL/1.0.1e PHP/5.5.6
Я подозреваю, что неинтегрированное поведение коренится где-то в причинах такого поведения заголовка содержимого. Но я не могу понять, как это происходит.
Итак, если кто-то знает больше о том, как они создаются, что может заставить их вести себя так или даже как решить проблему напрямую, любая помощь будет принята с благодарностью.
Наконец-то разобрался. Размещать его здесь на случай, если кто-то где-то столкнется с подобной проблемой и считает ее полезной
Так. Оказывается, проблемная строка была такой:
$response-sendHeaders()
Для двоичных файлов эта строка имеет решающее значение для обеспечения его работы. Текстовые файлы не нуждаются в этом, хотя на самом деле они будут ломаться, как показано выше. Я не выяснил точно, «почему», он ведет себя так. Но решение было просто проверить, является ли запрошенный файл текстовым или нет. (со сборкой в исключении для файлов .html, но это больше опциональная особенность / особенность проекта и, таким образом, может быть безопасно проигнорировано)
Вот как выглядит функция сейчас и работает нормально:
public function getForceDownloadResponse($file_path, $file_name){
$file_info = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($file_info, $file_path.$file_name);
$text = (substr(finfo_file($file_info, $file_path.$file_name), 0, 4) == 'text') ? 1 : 0;
finfo_close($file_info);
$response = new Response();
$response->headers->set('Cache-Control', 'private');
$response->headers->set('Content-type', $mime_type);
$response->headers->set('Content-Disposition', 'attachment; filename="' . $file_name . '"');
$response->headers->set('Content-length', filesize($file_path.$file_name));
if(!$text || $mime_type == 'text/html'){
$response->sendHeaders();
}
$response->setContent(readfile($file_path.$file_name));
return $response;
}
Поскольку я до сих пор не знаю, что на самом деле послужило причиной различного поведения, оно не так безопасно и надежно, как хотелось бы, но пока оно отлично справляется со своей задачей.
Других решений пока нет …