На веб-сайте на основе PHP я хочу отправить пользователям пакет для загрузки после того, как они заполнили короткую форму. Загрузка, инициированная сайтом, должна быть похожа на такие сайты, как download.com, на которых написано, что «загрузка начнется через мгновение».
Пара возможные подходы, о которых я знаю, и совместимость с браузером (на основе быстрого теста):
1) Сделайте window.open, указывающий на новый файл.
- FireFox 3 blocks this.
- IE6 blocks this.
- IE7 blocks this.
2) Создайте iframe, указывающий на новый файл.
- FireFox 3 seems to think this is OK. (Maybe it's because I already accepted it once?)
- IE6 blocks this.
- IE7 blocks this.
How can I do this so that at least these three browsers will not object?
Бонус: есть ли метод, который не требует условных операторов браузера?
(Я считаю, что download.com использует оба метода условно, но я не могу заставить работать ни один из них.)
Ответы и пояснения:
Q: "Why not point the current window to the file?"
A: That might work, but in this particular case, I want to show them some other content while their download starts - for example, "would you like to donate to this project?"
ОБНОВЛЕНИЕ: я отказался от этого подхода. См. Мой ответ ниже по причинам.






Как насчет изменения местоположения, чтобы указать на новый файл? (например, изменив window.location)
Обычно у меня есть PHP-скрипт, который выводит файл прямо в браузер с соответствующим Content-Type.
if (file_exists($filename)) {
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, pre-check=0");
header("Cache-Control: private", false);
header("Content-Type: " . $content-type);
header("Content-Disposition: attachment; filename=\"" . basename($filename) . "\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($filename));
readfile("$filename");
}else{
print "ERROR: the file " . basename($filename) . " could not be downloaded because it did not exist.";
}
Единственный недостаток заключается в том, что, поскольку он устанавливает заголовок HTTP, он вызывается до того, как у вас будет какой-либо другой вывод.
Но у вас может быть ссылка на страницу загрузки PHP, и это приведет к тому, что в браузере появится окно загрузки, не испортив содержимое текущей страницы.
По сути, вы читаете двоичное содержимое файла и отправляете эти двоичные данные в браузер вместо HTML. Content-Type просто сообщает браузеру, что это за файл. Попробуйте создать простую страницу, которая представляет собой просто мой фрагмент кода, и вставьте имя файла и тип содержимого.
Некоторые удобные типы контента, которые следует знать, это application / zip pplication / pdf application / octet-stream application / access application / msword application / vnd.ms-excel application / vnd.ms-powerpoint image / gif image / png image / jpg.
Вы проигнорировали часть вопроса. «Загрузка, инициированная сайтом, должна быть похожа на такие сайты, как download.com, на которых написано, что« загрузка начнется через мгновение ».
Примечание: вы можете получить приличный массив типов mime здесь snipplr.com/view.php?codeview&id=1937 в формате $ extension => $ mimeString
Вы также можете выполнить мета-обновление, которое поддерживает большинство браузеров. Download.com помещает один в тег noscript.
<meta http-equiv = "refresh" content = "5;url=/download.php?doc=123.zip"/>
Я ненавидеть тех. Мне нужно подождать несколько секунд загрузки, которая иногда занимает 1 секунду. Мой браузер сохраняет сеансы просмотра, что означает, что файлы начинают появляться из ниоткуда, когда я перезапускаю браузер. Я не могу скопировать ссылку или должен искать на странице «нажмите здесь, если ...» и останавливает автоматическую загрузку.
Согласился, но изначально он просил именно об этом.
Я всегда просто делал iframe, указывающий на файл.
<iframe src = "/download.exe" frameborder = "0" height = "0" width = "0"><a href = "/download.exe">Click here to download.</a></iframe>
По поводу того, что текущее окно не указывается на загрузку. По моему опыту, вы все еще можете показывать свою страницу с просьбой пожертвовать, поскольку загрузки (при условии, что они отправляют правильные заголовки) на самом деле не обновляют окно браузера. Я делаю это для экспорта csv на одном из моих сайтов, и с точки зрения пользователя это просто всплывает окно безопасного файла. Поэтому я бы порекомендовал простой мета-редирект, как показал Soldarnal.
Подводя итог, у вас есть 2 цели:
Для этого я бы сделал следующее:
Когда ваш пользователь отправляет форму, он получает страницу с вариантами пожертвования и текстом о том, что его загрузка начнется через 5 секунд. И в разделе заголовка этой страницы вы помещаете код META, как сказал Soldarnal: <meta http-Equiv = "refresh" content = "5; url = / download.php? doc = 123.zip>
И это все.
Одна загвоздка заключается в том, что вы можете столкнуться с проблемами с IE (в частности, с версией 6), если заголовки настроены неправильно.
Убедитесь, что вы установили правильный Content-Type, но также рассмотрите возможность установки параметров кэша для IE (как минимум) на кэширование позволять. Если это файл, который пользователь может открыть, а не сохранить (например, документ MS Word), ранним версиям IE необходимо кэшировать файл, поскольку они передают запрос «открытия» соответствующему приложению, указывая на загруженный файл. в кеше.
Существует также связанная с этим проблема: если кеш пользователя IE6 заполнен, он не сможет правильно сохранить файл (таким образом, когда применимое приложение получит руку, чтобы открыть его, оно будет жаловаться, что файл поврежден.
Вы также можете отключить любые действия gzip для загрузок (для IE)
У IE6 / IE7 есть проблемы с большими загрузками (например, 4.x Gigs ...), что маловероятно, поскольку в IE даже нет диспетчера загрузок, но есть кое-что, о чем следует знать.
Наконец, IE6 иногда плохо обрабатывает «push» загрузки, если он был инициирован из вложенного iframe. Я не совсем уверен, что вызывает проблему, но я считаю, что с IE6 легче избежать этого сценария.
Обновление: я решил отказаться от этого подхода и вместо этого просто предоставить пользователю ссылку на фактический файл. Мои рассуждения таковы:
Мои первоначальные попытки загрузки, инициированной сервером, были заблокированы браузером. Это заставило меня задуматься: «Браузер правильный. Как Это узнает, что это законная загрузка? Он должен блокирует загрузку, которая явно не инициирована пользователем».
Любой метод, который я могу использовать для загрузки, инициируемой сервером, может быть использован кем-то, кто хочет отправить вредоносное ПО. Следовательно, загрузка должна происходить только тогда, когда пользователь специально запрашивает файл, щелкнув ссылку для него.
Вы можете не согласиться, и если вы все же хотите начать загрузку, надеюсь, эта ветка поможет вам в этом.
Хой!
@Натан: Я решил сделать именно это: пусть мой "getfile.php" загрузит все необходимое, а затем выполнит
header("Location: ./$path/$filename");
чтобы позволить самому браузеру делать с файлом все, что он считает правильным. У меня это даже отлично работает в Opera.
Но это будет проблемой в средах, где прямой доступ к файлам не разрешен, в этом случае вам придется найти другой способ! (Слава Discordia, мои файлы являются общедоступными PDF-файлами!)
С уважением, Басти
<a href = "normaldownload.zip" onclick = "use_dhtml_or_ajax_to_display_page()">
Текущая страница не изменяется, если загрузка сохранена. Просто убедитесь, что загрузка не открывается в том же окне (правильный тип MIME или Content-Disposition), и вы сможете показать все, что угодно.
Вы можете использовать Javascript / jQuery, чтобы начать загрузку. Вот пример - вы можете избавиться от запроса Ajax и просто использовать блок setTimeout ().
$("btnDownloadCSV").on('click', function() {
$.ajax({
url: "php_backend/get_download_url",
type: 'post',
contentType: "application/x-www-form-urlencoded",
data: {somedata: "somedata"},
success: function(data) {
// If iFrame already exists, remove it.
if ($("[id^='iframeTempCSV_"]).length) {
$("[id^='iframeTempCSV_"]).remove();
}
setTimeout(function() {
// If I'm creating an iframe with the same id, it will permit download only the first time.
// So randHashId appended to ID to trick the browser.
var randHashId = Math.random().toString(36).substr(2);
// Create a fresh iFrame for auto-downloading CSV
$('<iframe id = "iframeTempCSV_'+randHashId+'" style = "display:none;" src = "'+data.filepath+'"></iframe>').appendTo('body');
}, 1000);
},
error: function(xhr, textStatus, errorThrown) {
console.error("Error downloading...");
}
});
});
Марк - это должно быть выше моей головы; Я ничего не понимаю в заголовках. Позволяет ли этот метод перейти на другую страницу во время загрузки?