Похоже, поиск в поиске проблем с xsendfile приводит к ряду совпадений, которые являются противоречивыми / устаревшими.
В целях полного раскрытия информации я беру бета-версию xsendfile 1.0, задокументированную на https://tn123.org/mod_xsendfile/beta/ (этот сайт не часто появляется в результатах поиска, только тот, который не содержит / beta). Я использую его с Apache 2.4 и php 5.4.34 на Linux и Windows. Помимо того, что это последняя версия, мне нужно использовать бета-версию, потому что только бета-версия имеет бинарные файлы Windows, созданные с VC9 для Apache 2.4.
Я сделал ошибку, прочитав документацию, где в описании значения имени файла в заголовке написано:
значение (имя файла), указанное в заголовке, считается закодированным в URL-адресе,
то есть будет выполнено удаление / декодирование URL. Смотрите XSendFileUnescape.
Если вам случится хранить файлы с использованием уже закодированных имен файлов, вы
должен «дважды» кодировать имена …% 20 ->% 2520
И описание XSendFIleUnescape гласит:
Отключение XSendFileUnescape восстановит поведение до версии 1.0
используя необработанное значение заголовка, вместо попытки unescape / url-decode
первый.
Документация об относительных путях ясно показывает, что имя файла на X-SendFile
заголовок должен быть полным путем. Поэтому я тщательно проверил свои пути через php urlencode
функция.
Конечным результатом для меня всегда была внутренняя ошибка сервера (код состояния 500) как в Linux, так и в Windows. Когда у меня была директива XSendFilePath в server config
контекст, о котором говорится в документации, разрешен, я не получил ничего более конкретного в своем журнале ошибок. Но когда я (в конце концов) переместил эту директиву Directory
в контексте я получил это в своем журнале ошибок:
(404)Unknown error: [client 127.0.0.1:20742] xsendfile: bad file name encoding
В конце концов, в отчаянии, я сказал: «Винт документации», и удалил urlencode
по пути. И вдруг он начал работать отлично (как Windows, так и Linux) !!!
У меня нет путей с символами не-ASCII, так что все готово. Но я удивляюсь, какой тип кодирования должен применяться, чтобы позволить работать не-ASCII-символам. Если вы гуглите xsendfile: bad file name encoding
Вы найдете следующий исходный код на https://github.com/nmaier/mod_xsendfile/blob/master/mod_xsendfile.c где эта строка сообщения создается путем принятия истинной ветви:
rv = ap_unescape_url(file);
if (rv != OK) {
Но я не могу найти хорошее описание или исходный код для ap_unescape_url()
, Если исходный код на github не устарел, эта функция возражает против простого кодирования% этого PHP urlencode()
Функция выполняет. Как дикое предположение, я пытался позвонить ap_escape_url()
, но это не определено в PHP. Так что остается вопрос о том, что такое кодировка предполагаемый для применения к параметру пути в X-SendFile
заголовок ??
Еще одно замечание / вопрос
Описание XSendFile
использование «apache internals» для отправки файла может заставить вас думать, что он создаст Content-Type
заголовок из расширения имени файла, используя mod_mime
, Но на самом деле это не так, и примеры показывают явное header()
звать Content-Type
, Итак, мое продолжение заключается в том, каков «правильный» способ создания этого заголовка из пути, передаваемого в X-SendFile
такой, что гарантированно соответствует тому, что mod_mime
будет делать, если мы не использовали X-SendFile
? Лучшее, что я мог придумать, это следующий код с использованием PHP fileinfo
расширение — но, насколько я знаю, нет особой причины ожидать, что оно будет фактически соответствовать тому, что делает Apache, когда ему дается URL для имени файла.
$finfo = new finfo(FILEINFO_MIME);
$mime_info = $finfo->file($pathname);
if (! strlen($mime_info)) {
$mime_info = 'application/octet-stream; charset=binary';
}
$basename = basename($pathname);
$encoded = "$pathname";
header("Content-Type: $mime_info");
header("Content-Disposition: attachment; filename=\"$basename\"");
header("X-SendFile: $encoded");
Я не собираюсь принимать это как ответ на вопрос, потому что я все еще не нашел «кодирующую функцию», которая, как ожидают, будет использоваться. Но я нашел эту древнюю документацию по API Apache: http://pedrowa.weba.sk/docs/ApiDoc/apidoc_ap_unescape_url.html. Это документирует возвращаемое значение ap_unescape_url () как:
Возвращает 0 в случае успеха, BAD_REQUEST, если обнаружена неверная escape-последовательность,
или NOT_FOUND, если% 2F (/) найден.
Но, конечно, в PHP и urlencode (), и rawurlencode () кодируют ‘/’ как% 2F.
Очевидно, что любой полный путь, используемый в заголовке XSendFile, должен НЕ быть закодированным с использованием любой из этих функций !!
Мое предположение относительно «лучшего» решения для меня:
$encoded = str_replace('%2F', '/', rawurlencode($pathname));
Я должен признать, что поражен тем, что в документации XSendFile об этом не говорится. И я еще больше удивляюсь тому, что на этот вопрос здесь нет ответов. Должен ли я разместить это на другом сайте Stack Exchange?
Других решений пока нет …