Загрузите файл jQuery.Ajax
У меня есть действие Struts2 на стороне сервера для загрузки файлов.
<action name="download" class="com.xxx.DownAction">
<result name="success" type="stream">
<param name="contentType">text/plain</param>
<param name="inputName">imageStream</param>
<param name="contentDisposition">attachment;filename={fileName}</param>
<param name="bufferSize">1024</param>
</result>
</action>
Однако, когда я вызываю действие с помощью jQuery:
$.post(
"/download.action",{
para1:value1,
para2:value2
....
},function(data){
console.info(data);
}
);
в Firebug Я вижу, что данные извлекаются с помощью Двоичного потока. Интересно, как открыть окно загрузки файлов, с помощью которого пользователь может локально сохранить файл?
Ответы
Ответ 1
2019 обновление современных браузеров
Такой подход я бы сейчас рекомендовал с несколькими оговорками:
- Требуется относительно современный браузер
- Если ожидается, что файл будет очень большим, вы, вероятно, должны сделать что-то похожее на оригинальный подход (iframe и cookie), потому что некоторые из приведенных ниже операций могут, вероятно, потреблять системную память по крайней мере так же, как загружаемый файл и/или другой интересный процессор побочные эффекты.
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
// the filename you want
a.download = 'todo-1.json';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
alert('your file has downloaded!'); // or you know, something with better UX...
})
.catch(() => alert('oh no!'));
Ответ 2
Никто не опубликовал это решение @Pekka... поэтому я опубликую его. Это может помочь кому-то.
Вам не нужно делать это через Ajax. Просто используйте
window.location="download.action?para1=value1...."
Ответ 3
Вы можете использовать HTML5
NB: возвращенные данные файла ДОЛЖНЫ быть закодированы в base64, потому что вы не можете кодировать двоичные данные JSON
В моем ответе AJAX
у меня есть структура данных, которая выглядит так:
{
result: 'OK',
download: {
mimetype: string(mimetype in the form 'major/minor'),
filename: string(the name of the file to download),
data: base64(the binary data as base64 to download)
}
}
Это означает, что я могу сделать следующее, чтобы сохранить файл через AJAX
var a = document.createElement('a');
if (window.URL && window.Blob && ('download' in a) && window.atob) {
// Do it the HTML5 compliant way
var blob = base64ToBlob(result.download.data, result.download.mimetype);
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = result.download.filename;
a.click();
window.URL.revokeObjectURL(url);
}
Функция base64ToBlob была взята из здесь и должна использоваться в соответствии с этой функцией
function base64ToBlob(base64, mimetype, slicesize) {
if (!window.atob || !window.Uint8Array) {
// The current browser doesn't have the atob function. Cannot continue
return null;
}
mimetype = mimetype || '';
slicesize = slicesize || 512;
var bytechars = atob(base64);
var bytearrays = [];
for (var offset = 0; offset < bytechars.length; offset += slicesize) {
var slice = bytechars.slice(offset, offset + slicesize);
var bytenums = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
bytenums[i] = slice.charCodeAt(i);
}
var bytearray = new Uint8Array(bytenums);
bytearrays[bytearrays.length] = bytearray;
}
return new Blob(bytearrays, {type: mimetype});
};
Это хорошо, если ваш сервер сбрасывает filedata для сохранения. Тем не менее, я не совсем понял, как реализовать резервную копию HTML4
Ответ 4
1. Агентский атрибут: файл загрузки сервлета в качестве вложения
<!-- with JS -->
<a href="javascript:window.location='downloadServlet?param1=value1'">
download
</a>
<!-- without JS -->
<a href="downloadServlet?param1=value1" >download</a>
2. Struts2 Framework: действие, загружающее файл в качестве вложения
<!-- with JS -->
<a href="javascript:window.location='downloadAction.action?param1=value1'">
download
</a>
<!-- without JS -->
<a href="downloadAction.action?param1=value1" >download</a>
Было бы лучше использовать <s:a>
указывающий с OGNL на URL, созданный с <s:url>
:
<!-- without JS, with Struts tags: THE RIGHT WAY -->
<s:url action="downloadAction.action" var="url">
<s:param name="param1">value1</s:param>
</s:ulr>
<s:a href="%{url}" >download</s:a>
В приведенных выше случаях вам нужно написать заголовок Content-Disposition для ответа, указав, что файл нужно загрузить (attachment
) и не открыть браузером (inline
). Вам также нужно указать Тип контента, и вы можете захотеть добавить имя и длину файла (чтобы браузер рисовал реалистичную панель прогресса).
Например, при загрузке ZIP:
response.setContentType("application/zip");
response.addHeader("Content-Disposition",
"attachment; filename=\"name of my file.zip\"");
response.setHeader("Content-Length", myFile.length()); // or myByte[].length...
С Struts2 (если вы не используете действие как сервлет, например, взлом для прямой потоковой передачи), вам не нужно напрямую писать что-либо в ответ; просто используя тип результата Stream и его настройку в struts.xml будет работать: ПРИМЕР
<result name="success" type="stream">
<param name="contentType">application/zip</param>
<param name="contentDisposition">attachment;filename="${fileName}"</param>
<param name="contentLength">${fileLength}</param>
</result>
3. Агентский агент (/Struts2 framework): файл открытия сервлета (/действия) внутри браузера
Если вы хотите открыть файл внутри браузера, вместо того, чтобы его загружать, для Content-disposition должно быть установлено значение inline, но цель не может быть текущим местоположением окна; вы должны настроить таргетинг на новое окно, созданное javascript, <iframe>
на странице, или новое окно, созданное "на лету" с "обсуждаемым" target = "_ blank":
<!-- From a parent page into an IFrame without javascript -->
<a href="downloadServlet?param1=value1" target="iFrameName">
download
</a>
<!-- In a new window without javascript -->
<a href="downloadServlet?param1=value1" target="_blank">
download
</a>
<!-- In a new window with javascript -->
<a href="javascript:window.open('downloadServlet?param1=value1');" >
download
</a>
Ответ 5
Я создал небольшую функцию в качестве решения обхода (вдохновленный плагином @JohnCulviner):
// creates iframe and form in it with hidden field,
// then submit form with provided data
// url - form url
// data - data to form field
// input_name - form hidden input name
function ajax_download(url, data, input_name) {
var $iframe,
iframe_doc,
iframe_html;
if (($iframe = $('#download_iframe')).length === 0) {
$iframe = $("<iframe id='download_iframe'" +
" style='display: none' src='about:blank'></iframe>"
).appendTo("body");
}
iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
if (iframe_doc.document) {
iframe_doc = iframe_doc.document;
}
iframe_html = "<html><head></head><body><form method='POST' action='" +
url +"'>" +
"<input type=hidden name='" + input_name + "' value='" +
JSON.stringify(data) +"'/></form>" +
"</body></html>";
iframe_doc.open();
iframe_doc.write(iframe_html);
$(iframe_doc).find('form').submit();
}
Демо с событием click:
$('#someid').on('click', function() {
ajax_download('/download.action', {'para1': 1, 'para2': 2}, 'dataname');
});
Ответ 6
Простой способ заставить браузер загружать файл - это сделать такой запрос:
function downloadFile(urlToSend) {
var req = new XMLHttpRequest();
req.open("GET", urlToSend, true);
req.responseType = "blob";
req.onload = function (event) {
var blob = req.response;
var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download=fileName;
link.click();
};
req.send();
}
Откроется всплывающее окно браузера.
Ответ 7
Хорошо, основываясь на коде ndpu, улучшена (я думаю) версия ajax_download; -
function ajax_download(url, data) {
var $iframe,
iframe_doc,
iframe_html;
if (($iframe = $('#download_iframe')).length === 0) {
$iframe = $("<iframe id='download_iframe'" +
" style='display: none' src='about:blank'></iframe>"
).appendTo("body");
}
iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
if (iframe_doc.document) {
iframe_doc = iframe_doc.document;
}
iframe_html = "<html><head></head><body><form method='POST' action='" +
url +"'>"
Object.keys(data).forEach(function(key){
iframe_html += "<input type='hidden' name='"+key+"' value='"+data[key]+"'>";
});
iframe_html +="</form></body></html>";
iframe_doc.open();
iframe_doc.write(iframe_html);
$(iframe_doc).find('form').submit();
}
Используйте это следующим образом:
$('#someid').on('click', function() {
ajax_download('/download.action', {'para1': 1, 'para2': 2});
});
Парамеры отправляются как правильные пост-параметры, как если бы они поступали из ввода, а не как кодированная json строка в соответствии с предыдущим примером.
CAVEAT: Будьте осторожны в отношении возможности переменной инъекции в этих формах. Может быть более безопасный способ кодирования этих переменных. Альтернативно, избегайте их.
Ответ 8
Я столкнулся с той же проблемой и успешно ее разрешил. Мой прецедент - это.
" Опубликовать данные JSON на сервере и получить файл excel.
Этот файл excel создается сервером и возвращается как ответ клиенту. Загрузите этот ответ как файл с пользовательским именем в браузере "
$("#my-button").on("click", function(){
// Data to post
data = {
ids: [1, 2, 3, 4, 5]
};
// Use XMLHttpRequest instead of Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
var a;
if (xhttp.readyState === 4 && xhttp.status === 200) {
// Trick for making downloadable link
a = document.createElement('a');
a.href = window.URL.createObjectURL(xhttp.response);
// Give filename you wish to download
a.download = "test-file.xls";
a.style.display = 'none';
document.body.appendChild(a);
a.click();
}
};
// Post data to URL which handles post request
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
});
Вышеприведенный фрагмент просто выполняет следующие
- Проводка массива как JSON на сервер с использованием XMLHttpRequest.
- После извлечения содержимого в виде blob (двоичного) мы создаем загружаемый URL-адрес и прикрепляем его к невидимой ссылке "a", а затем щелкаем по ней. Я сделал запрос POST здесь. Вместо этого вы можете пойти и для простого GET. Мы не можем загрузить файл через Ajax, должны использовать XMLHttpRequest.
Здесь нам нужно тщательно задать несколько вещей на стороне сервера. Я установил несколько заголовков в Python Django HttpResponse. Вы должны установить их соответственно, если используете другие языки программирования.
# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
Так как я загружаю xls (excel) здесь, я скорректировал contentType выше одного. Вам нужно установить его в соответствии с типом файла. Вы можете использовать эту технику для загрузки любых файлов.
Ответ 9
Вот что я сделал, чистый javascript и html. Не тестировал, но это должно работать во всех браузерах.
Функция Javascript
var iframe = document.createElement('iframe');
iframe.id = "IFRAMEID";
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.src = 'SERVERURL'+'?' + $.param($scope.filtro);
iframe.addEventListener("load", function () {
console.log("FILE LOAD DONE.. Download should start now");
});
Использование только компонентов, поддерживаемых во всех браузерах, не является дополнительным библиотеки.
![enter image description here]()
![enter image description here]()
Вот код контроллера JAVA Spring на стороне сервера.
@RequestMapping(value = "/rootto/my/xlsx", method = RequestMethod.GET)
public void downloadExcelFile(@RequestParam(value = "param1", required = false) String param1,
HttpServletRequest request, HttpServletResponse response)
throws ParseException {
Workbook wb = service.getWorkbook(param1);
if (wb != null) {
try {
String fileName = "myfile_" + sdf.format(new Date());
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + ".xlsx\"");
wb.write(response.getOutputStream());
response.getOutputStream().close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Ответ 10
function downloadURI(uri, name)
{
var link = document.createElement("a");
link.download = name;
link.href = uri;
link.click();
}
Ответ 11
Добавление еще нескольких вещей в ответ для загрузки файла
Ниже приведен код java spring, который генерирует байт Array
@RequestMapping(value = "/downloadReport", method = { RequestMethod.POST })
public ResponseEntity<byte[]> downloadReport(
@RequestBody final SomeObejct obj, HttpServletResponse response) throws Exception {
OutputStream out = new ByteArrayOutputStream();
// write something to output stream
HttpHeaders respHeaders = new HttpHeaders();
respHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
respHeaders.add("X-File-Name", name);
ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
return new ResponseEntity<byte[]>(bos.toByteArray(), respHeaders, HttpStatus.CREATED);
}
Теперь в javascript-коде, используя FileSaver.js, вы можете скачать файл с кодом ниже
var json=angular.toJson("somejsobject");
var url=apiEndPoint+'some url';
var xhr = new XMLHttpRequest();
//headers('X-File-Name')
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 201) {
var res = this.response;
var fileName=this.getResponseHeader('X-File-Name');
var data = new Blob([res]);
saveAs(data, fileName); //this from FileSaver.js
}
}
xhr.open('POST', url);
xhr.setRequestHeader('Authorization','Bearer ' + token);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.responseType = 'arraybuffer';
xhr.send(json);
Вышеупомянутый файл будет загружен
Ответ 12
В Rails я делаю так:
function download_file(file_id) {
let url = '/files/' + file_id + '/download_file';
$.ajax({
type: 'GET',
url: url,
processData: false,
success: function (data) {
window.location = url;
},
error: function (xhr) {
console.log(' Error: >>>> ' + JSON.stringify(xhr));
}
});
}
Трюк - это часть window.location. Метод контроллера выглядит так:
# GET /files/{:id}/download_file/
def download_file
send_file(@file.file,
:disposition => 'attachment',
:url_based_filename => false)
end
Ответ 13
Итак, вот рабочий код при использовании MVC и вы получаете свой файл с контроллера
позволяет сказать, что ваш массив байтов объявляется и заполняется, единственное, что вам нужно сделать, это использовать функцию File (используя System.Web.Mvc)
byte[] bytes = .... insert your bytes in the array
return File(bytes, System.Net.Mime.MediaTypeNames.Application.Octet, "nameoffile.exe");
а затем, в том же контроллере, добавьте thoses 2 функции
protected override void OnResultExecuting(ResultExecutingContext context)
{
CheckAndHandleFileResult(context);
base.OnResultExecuting(context);
}
private const string FILE_DOWNLOAD_COOKIE_NAME = "fileDownload";
/// <summary>
/// If the current response is a FileResult (an MVC base class for files) then write a
/// cookie to inform jquery.fileDownload that a successful file download has occured
/// </summary>
/// <param name="context"></param>
private void CheckAndHandleFileResult(ResultExecutingContext context)
{
if (context.Result is FileResult)
//jquery.fileDownload uses this cookie to determine that a file download has completed successfully
Response.SetCookie(new HttpCookie(FILE_DOWNLOAD_COOKIE_NAME, "true") { Path = "/" });
else
//ensure that the cookie is removed in case someone did a file download without using jquery.fileDownload
if (Request.Cookies[FILE_DOWNLOAD_COOKIE_NAME] != null)
Response.Cookies[FILE_DOWNLOAD_COOKIE_NAME].Expires = DateTime.Now.AddYears(-1);
}
а затем вы сможете вызвать своего контроллера для загрузки и получить обратный вызов "успех" или "отказ"
$.fileDownload(mvcUrl('name of the controller'), {
httpMethod: 'POST',
successCallback: function (url) {
//insert success code
},
failCallback: function (html, url) {
//insert fail code
}
});
Ответ 14
Если вы хотите использовать загрузку файла jQuery, обратите внимание на это для IE.
Вам нужно reset ответ или он не будет загружать
//The IE will only work if you reset response
getServletResponse().reset();
//The jquery.fileDownload needs a cookie be set
getServletResponse().setHeader("Set-Cookie", "fileDownload=true; path=/");
//Do the reset of your action create InputStream and return
Ваше действие может реализовать ServletResponseAware
для доступа к getServletResponse()
Ответ 15
Я нашел исправление, что, хотя на самом деле он не использует ajax, он позволяет вам использовать вызов javascript для запроса загрузки, а затем получить обратный вызов, когда загрузка начнется. Я нашел это полезным, если ссылка работает на стороне сервера script, которая занимает немного времени, чтобы составить файл перед его отправкой. поэтому вы можете предупредить их о том, что он обрабатывается, а затем, когда он наконец отправит файл, удалите это уведомление об обработке. поэтому я хотел попытаться загрузить файл через ajax, чтобы начать с того, чтобы я мог произойти событие, когда файл запрашивается, а другой, когда он действительно начинает загрузку.
js на первой странице
функция expdone()
{ . Document.getElementById( 'exportdiv') style.display = 'ни';
}
функция expgo()
{ . Document.getElementById( 'exportdiv') style.display = 'блок'; document.getElementById( 'exportif') src= 'test2.php аргументы = данные?.
}
Код>
iframe
< div id = "exportdiv" style = "display: none;" >
< img src= "loader.gif" > <br> <h1> Генерирующий отчет </h1>
< iframe id = "exportif" src= "style =" width: 1px; height: 1px; border: 0px;" > </iframe>
</дел >
Код>
то другой файл:
<! DOCTYPE html >
< & HTML GT;
< & головка GT;
< & сценарий GT;
функция expdone()
{ window.parent.expdone();
}
</сценарий >
</головка >
& Л; тело >
< iframe id = "exportif" src= "<? php echo" http://10.192.37.211/npdtracker/exportthismonth.php?arguments= ".$_GET[" arguments "];? >" > < lt;/IFRAME >
<script> document.getElementById('exportif'). onload = expdone; </script>
</тело > </HTML>
Код>
Я думаю, что есть способ читать данные с помощью js, поэтому php не понадобится. но я не знаю, это от руки, а сервер, который я использую, поддерживает php, поэтому это работает для меня. подумал, что я поделюсь им, если он кому-то поможет.
Ответ 16
Несомненно, вы не можете сделать это через вызов Ajax.
Однако есть обходной путь.
Шаги:
Если вы используете form.submit() для загрузки файла, то вы можете сделать следующее:
- Создайте ajax-вызов от клиента к серверу и сохраните поток файлов внутри сеанса.
- После того, как "успех" будет возвращен с сервера, вызовите форму.submit(), чтобы просто передать поток файлов, хранящийся в сеансе.
Это полезно в том случае, если вы хотите решить, нужно ли загружать файл после создания form.submit(), например: может быть случай, когда в form.submit() исключение возникает на стороне сервера и вместо этого сбоя, вам может потребоваться показать пользовательское сообщение на стороне клиента, в этом случае эта реализация может помочь.
Ответ 17
Я долго боролся с этой проблемой. Наконец, элегантная внешняя библиотека, предложенная здесь, выручила меня.
Ответ 18
Используйте window.open
https://developer.mozilla.org/en-US/docs/Web/API/Window/open
Например, вы можете поместить эту строку кода в обработчик кликов:
window.open('/file.txt', '_blank');
Откроется новая вкладка (из-за имени окна "_blank"), и эта вкладка откроет URL.
Ваш серверный код также должен иметь что-то вроде этого:
res.set('Content-Disposition', 'attachment; filename=file.txt');
Таким образом, браузер должен предлагать пользователю сохранить файл на диск, а не просто показывать им файл. Он также автоматически закроет вкладку, которую он только что открыл.
Ответ 19
есть другое решение для загрузки веб-страницы в ajax. Но я имею в виду страницу, которая должна быть сначала обработана, а затем загружена.
Для начала нужно отделить обработку страницы от загрузки результатов.
1) В вызове ajax выполняются только расчеты страницы.
$.post("CalculusPage.php", { calculusFunction: true, ID: 29, data1: "a", data2: "b" },
function(data, status)
{
if (status == "success")
{
/* 2) In the answer the page that uses the previous calculations is downloaded. For example, this can be a page that prints the results of a table calculated in the ajax call. */
window.location.href = DownloadPage.php+"?ID="+29;
}
}
);
// For example: in the CalculusPage.php
if ( !empty($_POST["calculusFunction"]) )
{
$ID = $_POST["ID"];
$query = "INSERT INTO ExamplePage (data1, data2) VALUES ('".$_POST["data1"]."', '".$_POST["data2"]."') WHERE id = ".$ID;
...
}
// For example: in the DownloadPage.php
$ID = $_GET["ID"];
$sede = "SELECT * FROM ExamplePage WHERE id = ".$ID;
...
$filename="Export_Data.xls";
header("Content-Type: application/vnd.ms-excel");
header("Content-Disposition: inline; filename=$filename");
...
Я надеюсь, что это решение может быть полезным для многих, как и для меня.