Unduh file oleh jQuery.Ajax

420

Saya memiliki tindakan Struts2 di sisi server untuk mengunduh file.

<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>

Namun ketika saya memanggil tindakan menggunakan jQuery:

$.post(
  "/download.action",{
    para1:value1,
    para2:value2
    ....
  },function(data){
      console.info(data);
   }
);

di Firebug saya melihat data diambil dengan aliran Biner . Saya bertanya-tanya bagaimana cara membuka jendela pengunduhan file yang dengannya pengguna dapat menyimpan file secara lokal?

hguser
sumber
1
Saya menandainya sebagai duplikat terlepas dari perbedaan platform, karena sejauh yang saya bisa lihat solusinya sama (Anda tidak dapat dan tidak perlu melakukan ini melalui Ajax).
Pekka
1
jadi, tanpa ajax, cukup gunakan window.location = "download.action? para1 = value1 ...."?
hguser

Jawaban:

676

2019 pembaruan browser modern

Inilah pendekatan yang sekarang saya rekomendasikan dengan beberapa peringatan:

  • Diperlukan browser yang relatif modern
  • Jika file diharapkan sangat besar, Anda mungkin harus melakukan sesuatu yang mirip dengan pendekatan asli (iframe dan cookie) karena beberapa operasi di bawah ini mungkin dapat mengkonsumsi memori sistem setidaknya sebesar file yang sedang diunduh dan / atau CPU menarik lainnya. efek samping.

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!'));

2012 Pendekatan berbasis jQuery / iframe / Cookie Asli

Kebiru - biruan benar tentang ini, Anda tidak dapat melakukannya melalui Ajax karena JavaScript tidak dapat menyimpan file secara langsung ke komputer pengguna (karena masalah keamanan). Sayangnya menunjuk URL jendela utama pada unduhan file Anda berarti Anda memiliki sedikit kendali atas apa yang dialami pengguna ketika unduhan file terjadi.

Saya membuat Unduhan File jQuery yang memungkinkan pengalaman "seperti Ajax" dengan unduhan file lengkap dengan panggilan balik OnSuccess dan OnFailure untuk memberikan pengalaman pengguna yang lebih baik. Lihatlah posting blog saya tentang masalah umum yang diselesaikan plugin dan beberapa cara untuk menggunakannya dan juga demo dari Unduhan File jQuery dalam aksi . Ini sumbernya

Berikut ini adalah contoh penggunaan sederhana menggunakan sumber plugin dengan janji. The halaman demo mencakup banyak lainnya, 'lebih baik UX' contoh juga.

$.fileDownload('some/file.pdf')
    .done(function () { alert('File download a success!'); })
    .fail(function () { alert('File download failed!'); });

Bergantung pada browser apa yang perlu Anda dukung, Anda mungkin dapat menggunakan https://github.com/eligrey/FileSaver.js/ yang memungkinkan kontrol lebih eksplisit daripada metode IFRAME yang digunakan oleh jQuery File Download.

John Culviner
sumber
69
Saya suka apa yang Anda buat, tetapi saya menduga bahwa untuk mendapatkan kredit StackOverFlow lebih banyak, jawaban Anda di sini harus memuat sedikit lebih detail. Khususnya tentang cara Anda memecahkan masalah.
AnthonyVO
14
alangkah baiknya jika Anda akan menyebutkan dengan tepat bagaimana "plugin" ini mengatasi batasan, daripada memaksa kami untuk pergi ke blog / sumber plugin Anda untuk melihatnya. misalnya, bukankah itu memposting ke iframe? apakah itu memerlukan skrip jarak jauh untuk menyimpan file dan mengembalikan url ke sana?
Kevin B
2
@asgerhallas Tentu, tapi itu sama sekali tidak berguna jika tautan tersebut hilang.
Kevin B
26
Saya setuju, blog adalah tempat yang jauh lebih baik untuk menempatkan deskripsi panjang tentang cara menggunakan plugin Anda dan cara kerjanya. tetapi Anda setidaknya bisa memberi gambaran singkat tentang bagaimana plugin ini memecahkan masalah. Sebagai contoh, ini memecahkan masalah dengan meminta server mengatur cookie dan membuat javascript Anda terus mencari cookie sampai ada. Setelah ada, kita dapat mengasumsikan bahwa pengunduhan selesai. Dengan informasi semacam itu orang dapat dengan mudah menggulung solusi mereka sendiri dengan sangat cepat, dan jawabannya tidak lagi bergantung pada blog / plugin / jquery Anda dan dapat diterapkan ke perpustakaan lain.
Kevin B
1
Royi, seperti yang saya mengerti, AJAX tidak pernah dapat mendukung unduhan file yang menghasilkan popup unduhan file untuk disimpan ke disk. Apakah Anda menemukan cara yang tidak saya sadari?
John Culviner
228

Tidak ada yang memposting solusi @ Pekka ini ... jadi saya akan mempostingnya. Itu bisa membantu seseorang.

Anda tidak perlu melakukan ini melalui Ajax. Gunakan saja

window.location="download.action?para1=value1...."
kebiruan
sumber
4
Bagus ... karena saya kesulitan menangani file unduh dan menggunakan jquery ajax..dan solusi ini berfungsi dengan baik untuk saya .. + 1
swapnesh
45
Perhatikan bahwa ini mengharuskan server untuk menetapkan nilai tajuk Content-Disposition dari 'attachment', jika tidak browser akan mengarahkan ulang ke (dan menampilkan) konten respons
brichins
21
Atau gunakan alternatif window.open(<url>, '_blank');untuk memastikan bahwa unduhan tidak akan menggantikan konten browser Anda saat ini (terlepas dari header Content-Disposition).
Christopher King
4
Masalah dengan solusi ini adalah bahwa jika operasi gagal / server mengembalikan kesalahan, halaman Anda akan diarahkan ke halaman kesalahan. Untuk mengatasinya gunakan solusi iFrame
kofifus
4
Masalah sebenarnya dengan solusi ini - pertanyaannya adalah tentang POSTpermintaan.
Atomosk
35

Anda bisa menggunakan HTML5

NB: Data file yang dikembalikan HARUS base64 disandikan karena Anda tidak bisa menyandikan data biner JSON

Dalam AJAXtanggapan saya, saya memiliki struktur data yang terlihat seperti ini:

{
    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)
    }
}

Itu berarti bahwa saya dapat melakukan hal berikut untuk menyimpan file melalui 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);
}

Function base64ToBlob diambil dari sini dan harus digunakan sesuai dengan fungsi ini

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});
};

Ini bagus jika server Anda sedang membuang data yang disimpan. Namun, saya belum mengetahui bagaimana seseorang akan mengimplementasikan HTML4 fallback

Luke Madhanga
sumber
1
The a.click()tampaknya tidak bekerja di firefox ... Setiap ide?
bigpony
Di beberapa browser Anda mungkin perlu menambahkan adom ke agar kode ini bekerja dan / atau menghapus revokeObjectURLbagian:document.body.appendChild(a)
bigpony
menyelamatkan hari saya (dan mungkin juga pekerjaan :)) Bukan ahli javascript dengan ukuran apa pun ... lebih java guy. Namun, saya tidak tahu mengapa "createObjectURL (Blob (new atob (base64)]))" yang sederhana tidak berfungsi! Itu tidak benar, sementara semua naluri mengatakan itu harus. grrr ...
apil.tamang
pada baris var bytechars = atob(base64)itu melempar kesalahan JavaScript runtime error: InvalidCharacterError. Saya menggunakan Chrome Versi 75.0.3770.142 tetapi saya tidak tahu, apa yang salah di sini.
Muflix
27

1. Framework agnostic: Servlet mengunduh file sebagai lampiran

<!-- with JS -->
<a href="javascript:window.location='downloadServlet?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadServlet?param1=value1" >download</a>

2. Struts2 Framework: Tindakan mengunduh file sebagai lampiran

<!-- with JS -->
<a href="javascript:window.location='downloadAction.action?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadAction.action?param1=value1" >download</a>

Akan lebih baik menggunakan <s:a>penunjuk tag dengan OGNL ke URL yang dibuat dengan <s:url>tag:

<!-- 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>

Dalam kasus di atas, Anda perlu menulis tajuk Content-Disposition ke respons , menentukan bahwa file tersebut perlu diunduh ( attachment) dan tidak dibuka oleh browser ( inline). Anda perlu menentukan Tipe Konten juga, dan Anda mungkin ingin menambahkan nama dan panjang file (untuk membantu browser menggambar progressbar yang realistis).

Misalnya, saat mengunduh 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...

Dengan Struts2 (kecuali jika Anda menggunakan Action sebagai Servlet, hack untuk streaming langsung , misalnya), Anda tidak perlu langsung menulis apa pun ke respons; cukup gunakan tipe hasil Stream dan konfigurasikan dalam struts.xml akan berfungsi: CONTOH

<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. Framework agnostic (/ Struts2 framework): Servlet (/ Action) membuka file di dalam browser

Jika Anda ingin membuka file di dalam browser, alih-alih mengunduhnya, disposisi Konten harus diatur ke inline , tetapi target tidak boleh lokasi jendela saat ini; Anda harus menargetkan jendela baru yang dibuat oleh javascript, <iframe>halaman, atau jendela baru yang dibuat saat itu dengan "mendiskusikan" 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>
Andrea Ligios
sumber
2
Pak, masukan Anda: "Content-Disposition", "inline; .... menyelamatkan hari coder yang malang :)
Vedran Maricevic
1
Ini adalah satu-satunya jawaban yang menyebutkan "window.open" (salah satu komentar menyebutkannya).
Andrew Koster
Ini tidak berfungsi jika Anda memiliki banyak parameter, karena Anda akan mendapatkan too long urlkesalahan.
Muflix
25

Cara sederhana untuk membuat browser mengunduh file adalah dengan membuat permintaan seperti itu:

 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();
 }

Ini membuka pop up unduhan browser.

João Marcos
sumber
3
Terima kasih, saya menggunakan solusi ini. Bekerja seperti pesona. Juga, jika Anda tidak mendapatkan gumpalan dari respons, cukup buat gumpalan baru.
fabio.sang
6
Versi yang lebih baik dengan tautan
startsWith_R
Tautan dari @startsWith_R sangat membantu jika Anda bekerja dengan
IE11
Terima kasih itu berhasil untuk saya!
Zaki Mohammed
23

Saya telah membuat sedikit fungsi sebagai solusi penyelesaian masalah (terinspirasi oleh plugin @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();
}

Demo dengan acara klik:

$('#someid').on('click', function() {
    ajax_download('/download.action', {'para1': 1, 'para2': 2}, 'dataname');
});
ndpu
sumber
Itu mengirimkan data dengan cara yang sangat aneh ke server sekalipun. Saya ingin tahu apakah itu dapat diubah untuk membuat POST yang sesuai?
Shayne
16

Saya menghadapi masalah yang sama dan berhasil menyelesaikannya. Kasing saya adalah ini.

" Kirim data JSON ke server dan terima file excel. File excel itu dibuat oleh server dan dikembalikan sebagai respons kepada klien. Unduh respons itu sebagai file dengan nama khusus di browser "

$("#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));
});

Cuplikan di atas hanya melakukan yang berikut

  • Posting array sebagai JSON ke server menggunakan XMLHttpRequest.
  • Setelah mengambil konten sebagai gumpalan (biner), kami membuat URL yang dapat diunduh dan melampirkannya ke tautan "a" yang tidak terlihat kemudian mengkliknya. Saya melakukan permintaan POST di sini. Sebagai gantinya, Anda juga bisa menggunakan GET sederhana. Kami tidak dapat mengunduh file melalui Ajax, harus menggunakan XMLHttpRequest.

Di sini kita perlu hati-hati mengatur beberapa hal di sisi server. Saya menetapkan beberapa header di Python Django HttpResponse. Anda perlu mengaturnya jika Anda menggunakan bahasa pemrograman lain.

# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

Karena saya mengunduh xls (excel) di sini, saya menyesuaikan contentType dengan yang di atas. Anda perlu mengaturnya sesuai dengan jenis file Anda. Anda dapat menggunakan teknik ini untuk mengunduh semua jenis file.

Naren Yellavula
sumber
"Kami tidak dapat mengunduh file melalui Ajax, harus menggunakan XMLHttpRequest". XMLHttpRequest adalah AJAX menurut definisi. Sebaliknya solusi hebat untuk browser web modern. Untuk IE, yang tidak mendukung HTMLAnchorElement.download, saya berpikir untuk menggabungkannya dengan metode msSaveOrOpenBlob .
Tsahi Asher
15

Ok, berdasarkan kode ndpu di sini adalah versi ajax_download yang ditingkatkan (saya pikir); -

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();
}

Gunakan ini seperti ini; -

$('#someid').on('click', function() {
    ajax_download('/download.action', {'para1': 1, 'para2': 2});
});

Params dikirim sebagai params posting yang tepat seolah-olah berasal dari input daripada sebagai string json yang dikodekan seperti contoh sebelumnya.

CAVEAT: Waspadai potensi injeksi variabel pada formulir-formulir itu. Mungkin ada cara yang lebih aman untuk menyandikan variabel-variabel itu. Atau pertimbangkan untuk melarikan diri dari mereka.

Shayne
sumber
Ini contoh yang bagus. Terima kasih. Apakah mungkin melakukannya tanpa iframe tetapi tanpa window.location?
Marek Bar
Saya kira Anda bisa menambahkan formulir tersembunyi ke bagian bawah DOM. Juga mungkin perlu ditelusuri adalah penggunaan dom Shadow, meskipun itu belum tentu didukung dengan baik pada browser lama.
Shayne
Dalam kode ini saya mendapatkan kesalahan ini. Uncaught SecurityError: Blocked a frame with origin "http://foo.bar.com" from accessing a frame with origin "null". The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "data". Protocols must match.
batal
Bagaimana saya bisa memetakan formulir ini ke beberapa kelas model? Saya punya: @ResourceMapping() public void downloadFile(final ResourceRequest request, final ResourceResponse response, @ModelAttribute("downForm") FormModel model) tetapi tidak berfungsi ..
bartex9
batal: Itu kemungkinan akan menjadi semacam masalah keamanan lintas asal. Itu mungkin seluruh tumpukan pertanyaan melimpah di dan dari itu sendiri. @ bartex9: Itu akan sangat bergantung pada kerangka kerja seperti apa yang Anda gunakan. Tetapi prinsipnya adalah untuk mengambil nama dan jalur dan menyimpannya, sambil mendorong file itu sendiri ke area yang dapat diakses web dari sistem file, atau sesuatu seperti amazon S3 untuk ketersediaan tinggi
Shayne
8

Inilah yang saya lakukan, javascript dan html murni. Tidak mengujinya tetapi ini harus bekerja di semua browser.

Fungsi 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");
});

Menggunakan hanya komponen yang didukung di semua browser tidak ada perpustakaan tambahan.

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Berikut ini adalah kode pengendali sisi server JAVA Spring saya.

@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();
        }
    }
    }
manukyanv07
sumber
sepertinya acara pemuatan Anda tidak dipanggil untuk konten lampiran disposisi Konten (karena tidak ada yang dimuat ke iframe), jika itu berfungsi untuk Anda (Anda mendapatkan console.log) silakan memposting sampel
kofifus
Berikut ini adalah jsfiddle.net/y2xezyoj biola cepat ini akan memunculkan acara pemuatan segera setelah file pdf dimuat ke iframe .. biola ini tidak mengunduh karena kunci untuk mengunduh ada di sisi server "response.setHeader (" Konten -disposisi "," attachment ; filename = \ "" + fileName + ".xlsx \" ");"
manukyanv07
1
ya itu akan bekerja dalam kasus itu, tetapi jika file tersebut diunduh, itu adalah server mengirimkan Content-Disposition: attachment, maka acara pemuatan tidak akan memecat yang merupakan poin saya
kofifus
Anda benar-benar benar memuat acara dipecat tepat setelah server selesai memproses mulai mengirim file. Inilah yang saya cari, 1 - blok tombol dan tampilkan pemrosesan sehingga pengguna dapat memiliki umpan balik bahwa sesuatu sedang terjadi. 2 - Kemudian ketika server selesai memproses dan akan mengirim file 3- (memuat acara dipecat) tempat saya membuka kunci tombol dan menghapus pemrosesan pemintal 4 - pengguna sekarang muncul dengan menyimpan file atau browser mulai mengunduhnya di lokasi unduhan yang ditentukan. Maaf bahasa inggris saya
manukyanv07
5
function downloadURI(uri, name) 
{
    var link = document.createElement("a");
    link.download = name;
    link.href = uri;
    link.click();
}
EL missaoui habib
sumber
Bisakah Anda menjelaskan jawaban Anda? Itu akan membantu orang lain memahami apa yang telah Anda lakukan sehingga mereka dapat menerapkan teknik Anda pada situasi mereka.
Wai Ha Lee
2
Hanya peringatan: Safari dan IE tidak mendukung downloadatribut, sehingga file Anda akan berakhir dengan nama "Tidak Dikenal"
Yangshun Tay
4

Di Rails, saya melakukannya dengan cara ini:

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));
    }
   });
 }

Caranya adalah bagian window.location . Metode pengontrol terlihat seperti:

# GET /files/{:id}/download_file/
def download_file
    send_file(@file.file,
          :disposition => 'attachment',
          :url_based_filename => false)
end
aarkerio
sumber
2
Pertanyaan cepat, bukankah ini akan menghasilkan file dua kali? Setelah Anda mengirim permintaan ajax. Lalu Anda membuat halaman redirect ke URL yang sama juga. Bagaimana kita bisa menghilangkannya?
coderh
Tidak dalam kasus saya. Saya hanya mengujinya di Chrome.
aarkerio
Karena coderh sudah menyatakan dengan benar, tindakan dipanggil dua kali.
Sven
Sudah dipanggil dua kali untuk saya juga.
CSquared
4

Gunakan window.open https://developer.mozilla.org/en-US/docs/Web/API/Window/open

Misalnya, Anda dapat meletakkan baris kode ini di penangan klik:

window.open('/file.txt', '_blank');

Ini akan membuka tab baru (karena nama jendela '_blank') dan tab itu akan membuka URL.

Kode sisi server Anda juga harus memiliki sesuatu seperti ini:

res.set('Content-Disposition', 'attachment; filename=file.txt');

Dan dengan demikian, browser harus meminta pengguna untuk menyimpan file ke disk, bukan hanya menunjukkan file kepada mereka. Ini juga akan secara otomatis menutup tab yang baru saja dibuka.

Andrew Koster
sumber
4

Saya mencoba mengunduh file CSV dan kemudian melakukan sesuatu setelah unduhan selesai. Jadi saya perlu menerapkan callbackfungsi yang sesuai .

Menggunakannya window.location="..."bukan ide yang baik karena saya tidak bisa mengoperasikan program setelah selesai mengunduh. Sesuatu seperti ini, ganti tajuk jadi itu bukan ide yang bagus.

fetchadalah alternatif yang baik namun tidak dapat mendukung IE 11 . Dan window.URL.createObjectURLtidak dapat mendukung IE 11. Anda dapat merujuk ini .

Ini kode saya, mirip dengan kode Shahrukh Alam. Tetapi Anda harus berhati-hati yang window.URL.createObjectURLmungkin membuat kebocoran memori. Anda bisa merujuk ini . Ketika respons telah tiba, data akan disimpan ke dalam memori browser. Jadi sebelum Anda mengklik atautan, file tersebut telah diunduh. Ini berarti Anda dapat melakukan apa saja setelah mengunduh.

$.ajax({
    url: 'your download url',
    type: 'GET',
}).done(function (data, textStatus, request) {
    // csv => Blob
    var blob = new Blob([data]);

    // the file name from server.
    var fileName = request.getResponseHeader('fileName');

    if (window.navigator && window.navigator.msSaveOrOpenBlob) { // for IE
    window.navigator.msSaveOrOpenBlob(blob, fileName);
    } else { // for others
    var url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = url;
    a.download = fileName;
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);

    //Do something after download 
    ...

    }
}).then(after_download)
}
kyakya
sumber
4

Cara MENGUNDUH file setelah menerimanya oleh AJAX

Lebih nyaman ketika file dibuat untuk waktu yang lama dan Anda harus menunjukkan PRELOADER

Contoh saat mengirimkan formulir web:

<script>
$(function () {
    $('form').submit(function () {
        $('#loader').show();
        $.ajax({
            url: $(this).attr('action'),
            data: $(this).serialize(),
            dataType: 'binary',
            xhrFields: {
                'responseType': 'blob'
            },
            success: function(data, status, xhr) {
                $('#loader').hide();
                // if(data.type.indexOf('text/html') != -1){//If instead of a file you get an error page
                //     var reader = new FileReader();
                //     reader.readAsText(data);
                //     reader.onload = function() {alert(reader.result);};
                //     return;
                // }
                var link = document.createElement('a'),
                    filename = 'file.xlsx';
                // if(xhr.getResponseHeader('Content-Disposition')){//filename 
                //     filename = xhr.getResponseHeader('Content-Disposition');
                //     filename=filename.match(/filename="(.*?)"/)[1];
                //     filename=decodeURIComponent(escape(filename));
                // }
                link.href = URL.createObjectURL(data);
                link.download = filename;
                link.click();
            }
        });
        return false;
    });
});
</script>

Fungsional opsional dikomentari untuk menyederhanakan contoh.

Tidak perlu membuat file sementara di server.

Pada jQuery v2.2.4 OK. Akan ada kesalahan pada versi lama:

Uncaught DOMException: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'blob').
Mike S
sumber
Untuk mendapatkan nama file dari Content-Disposition, pertandingan ini cocok untuk saya: filename.match(/filename=(.*)/)[1](tanpa tanda kutip ganda atau tanda tanya) - regex101.com/r/2AsD4y/2 . Namun, solusi Anda adalah satu-satunya solusi yang berfungsi setelah mencari banyak.
jstuardo
3

Menambahkan beberapa hal lagi ke jawaban di atas untuk mengunduh file

Di bawah ini adalah beberapa kode pegas java yang menghasilkan byte 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);
    }

Sekarang dalam kode javascript menggunakan FileSaver.js, dapat mengunduh file dengan kode di bawah ini

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);

Di atas akan mengunduh file

dario nascimento
sumber
2

Ok jadi di sini adalah kode yang berfungsi ketika Menggunakan MVC dan Anda mendapatkan file Anda dari controller

katakanlah Anda memiliki byte array Anda menyatakan dan mengisi, satu-satunya hal yang perlu Anda lakukan adalah menggunakan fungsi File (menggunakan System.Web.Mvc)

byte[] bytes = .... insert your bytes in the array
return File(bytes, System.Net.Mime.MediaTypeNames.Application.Octet, "nameoffile.exe");

dan kemudian, dalam pengontrol yang sama, tambahkan fungsi mereka

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);
    }

dan kemudian Anda akan dapat memanggil pengontrol Anda untuk mengunduh dan mendapatkan panggilan balik "sukses" atau "gagal"

$.fileDownload(mvcUrl('name of the controller'), {
            httpMethod: 'POST',
            successCallback: function (url) {
            //insert success code

            },
            failCallback: function (html, url) {
            //insert fail code
            }
        });
Yannick Richard
sumber
1

Saya menemukan perbaikan bahwa sementara itu sebenarnya tidak menggunakan ajax itu memungkinkan Anda untuk menggunakan panggilan javascript untuk meminta unduhan dan kemudian mendapatkan panggilan balik ketika unduhan sebenarnya dimulai. Saya menemukan ini membantu jika tautan menjalankan skrip sisi server yang memerlukan sedikit waktu untuk menyusun file sebelum mengirimnya. sehingga Anda dapat memberi tahu mereka bahwa itu sedang diproses, dan kemudian ketika akhirnya mengirim file hapus pemberitahuan pemrosesan itu. itulah sebabnya saya ingin mencoba memuat file melalui ajax untuk memulainya sehingga saya dapat memiliki suatu peristiwa terjadi ketika file diminta dan yang lain ketika itu benar-benar mulai mengunduh.

js di halaman depan

function expdone()
{
    document.getElementById('exportdiv').style.display='none';
}
function expgo()
{
   document.getElementById('exportdiv').style.display='block';
   document.getElementById('exportif').src='test2.php?arguments=data';
}

iframe

<div id="exportdiv" style="display:none;">
<img src="loader.gif"><br><h1>Generating Report</h1>
<iframe id="exportif" src="" style="width: 1px;height: 1px; border:0px;"></iframe>
</div>

lalu file lainnya:

<!DOCTYPE html>
<html>
<head>
<script>
function expdone()
{
    window.parent.expdone();
}
</script>
</head>
<body>
<iframe id="exportif" src="<?php echo "http://10.192.37.211/npdtracker/exportthismonth.php?arguments=".$_GET["arguments"]; ?>"></iframe>
<script>document.getElementById('exportif').onload= expdone;</script>
</body></html>

Saya pikir ada cara untuk membaca mendapatkan data menggunakan js sehingga tidak diperlukan php. tapi saya tidak mengetahuinya begitu saja dan server yang saya gunakan mendukung php jadi ini bekerja untuk saya. pikir saya akan berbagi kalau-kalau itu membantu siapa pun.

Kit Ramos
sumber
0

Jika Anda ingin menggunakan Unduhan File jQuery, harap perhatikan ini untuk IE. Anda perlu mengatur ulang responsnya atau tidak akan mengunduh

    //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

Tindakan Anda dapat diterapkan ServletResponseAware untuk mengaksesgetServletResponse()

Alireza Fattahi
sumber
0

Sudah pasti Anda tidak bisa melakukannya melalui panggilan Ajax.

Namun, ada solusinya.

Langkah :

Jika Anda menggunakan form.submit () untuk mengunduh file, yang dapat Anda lakukan adalah:

  1. Buat panggilan ajax dari klien ke server dan simpan aliran file di dalam sesi.
  2. Setelah "berhasil" dikembalikan dari server, panggil form.submit () Anda untuk melakukan streaming aliran file yang disimpan dalam sesi.

Ini berguna jika Anda ingin memutuskan apakah file perlu diunduh atau tidak setelah membuat form.submit (), misalnya: ada kasus di form.submit (), pengecualian terjadi di sisi server dan sebagai gantinya crash, Anda mungkin perlu menampilkan pesan khusus di sisi klien, dalam hal ini implementasi ini mungkin membantu.

Aman Srivastava
sumber
0

ada solusi lain untuk mengunduh halaman web di ajax. Tetapi saya merujuk ke halaman yang pertama-tama harus diproses dan kemudian diunduh.

Pertama, Anda perlu memisahkan pemrosesan halaman dari hasil unduhan.

1) Hanya penghitungan halaman yang dibuat dalam panggilan ajax.

$ .post ("CalculusPage.php", {calculusFunction: true, ID: 29, data1: "a", data2: "b"},

       fungsi (data, status) 
       {
            if (status == "berhasil") 
            {
                / * 2) Pada jawaban halaman yang menggunakan perhitungan sebelumnya diunduh. Misalnya, ini bisa berupa halaman yang mencetak hasil dari tabel yang dihitung dalam panggilan ajax. * /
                window.location.href = UnduhPage.php + "? ID =" + 29;
            }               
       }
);

// Sebagai contoh: di CalculusPage.php

    if (! empty ($ _ POST ["calculusFunction"])) 
    {
        $ ID = $ _POST ["ID"];

        $ query = "INSERT INTO ExamplePage (data1, data2) VALUES ('". $ _ POST ["data1"]. "', '". $ _ POST ["data2"]. "') WHERE id =". $ ID;
        ...
    }

// Sebagai contoh: di DownloadPage.php

    $ ID = $ _GET ["ID"];

    $ sede = "SELECT * DARI ExamplePage WHERE id =". $ ID;
    ...

    $ filename = "Export_Data.xls";
    header ("Tipe-Konten: application / vnd.ms-excel");
    header ("Content-Disposition: inline; filename = $ filename");

    ...

Saya berharap solusi ini dapat bermanfaat bagi banyak orang, seperti juga bagi saya.

netluke
sumber
0
The HTML Code:-

'<button type="button" id="GetFile">Get File!</button>'


The jQuery Code:-

'$('#GetFile').on('click', function () {
    $.ajax({
        url: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/172905/test.pdf',
        method: 'GET',
        xhrFields: {
            responseType: 'blob'
        },
        success: function (data) {
            var a = document.createElement('a');
            var url = window.URL.createObjectURL(data);
            a.href = url;
            a.download = 'myfile.pdf';
            document.body.append(a);
            a.click();
            a.remove();
            window.URL.revokeObjectURL(url);
        }
    });
});'
Shahrukh Alam
sumber
Kode hanya jawaban harus memiliki setidaknya deskripsi minumum yang menjelaskan cara kerja kode dan mengapa itu menjawab pertanyaan.
Roberto Caboni
0

Itu berfungsi dengan sangat baik di browser apa pun (saya menggunakan asp.net core)

            function onDownload() {

  const api = '@Url.Action("myaction", "mycontroller")'; 
  var form = new FormData(document.getElementById('form1'));

  fetch(api, { body: form, method: "POST"})
      .then(resp => resp.blob())
      .then(blob => {
          const url = window.URL.createObjectURL(blob);
        $('#linkdownload').attr('download', 'Attachement.zip');
          $('#linkdownload').attr("href", url);
          $('#linkdownload')
              .fadeIn(3000,
                  function() { });

      })
      .catch(() => alert('An error occurred'));



}
 
 <button type="button" onclick="onDownload()" class="btn btn-primary btn-sm">Click to Process Files</button>
 
 
 
 <a role="button" href="#" style="display: none" class="btn btn-sm btn-secondary" id="linkdownload">Click to download Attachments</a>
 
 
 <form asp-controller="mycontroller" asp-action="myaction" id="form1"></form>
 
 

        function onDownload() {
            const api = '@Url.Action("myaction", "mycontroller")'; 
            //form1 is your id form, and to get data content of form
            var form = new FormData(document.getElementById('form1'));

            fetch(api, { body: form, method: "POST"})
                .then(resp => resp.blob())
                .then(blob => {
                    const url = window.URL.createObjectURL(blob);
                    $('#linkdownload').attr('download', 'Attachments.zip');
                    $('#linkdownload').attr("href", url);
                    $('#linkdownload')
                        .fadeIn(3000,
                            function() {

                            });
                })
                .catch(() => alert('An error occurred'));                 

        }
Marinpietri
sumber
-1

Saya berjuang dengan masalah ini untuk waktu yang lama. Akhirnya perpustakaan eksternal yang elegan yang disarankan di sini membantu saya.

Eerik Sven Puudist
sumber