JQuery.Ajax tarafından dosya indir


420

Dosya indirmek için sunucu tarafında Struts2 eylemim var.

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

Ancak ben jQuery kullanarak eylem çağırdığınızda:

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

Firebug'da verilerin İkili akışla alındığını görüyorum . Kullanıcının dosyayı yerel olarak kaydedebileceği dosya indirme penceresini nasıl açacağımı merak ediyorum ?



1
Platform farkına rağmen bunu bir kopya olarak işaretledim, çünkü görebildiğim kadarıyla çözüm aynı (Ajax aracılığıyla bunu yapamazsınız ve yapmanıza gerek yoktur).
Pekka

1
ajax olmadan, sadece window.location = "download.action? para1 = value1 ...."?
hguser

Yanıtlar:


676

2019 Modern Tarayıcılar Güncellemesi

Şimdi birkaç uyarı ile önereceğim yaklaşım budur:

  • Nispeten modern bir tarayıcı gerekli
  • Dosyanın çok büyük olması bekleniyorsa , muhtemelen orijinal yaklaşıma (iframe ve çerez) benzer bir şey yapmalısınız, çünkü aşağıdaki işlemlerden bazıları sistem belleğini en az indirilen dosya ve / veya diğer ilginç CPU kadar büyük tüketebilir. yan etkiler.

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 Orijinal jQuery / iframe / Çerez tabanlı yaklaşım

Mavimsi bu konuda tamamen doğru, Ajax aracılığıyla yapamazsınız çünkü JavaScript dosyaları doğrudan bir kullanıcının bilgisayarına kaydedemez (güvenlik endişeleri dışında). Ne yazık ki ana pencerenin URL'sini dosya indirme işleminize yönlendirmek, dosya indirme işlemi gerçekleştiğinde kullanıcı deneyiminin ne olduğu konusunda çok az kontrole sahip olduğunuz anlamına gelir.

Daha iyi bir kullanıcı deneyimi sağlamak için OnSuccess ve OnFailure geri çağrılarıyla tamamlanmış "Ajax benzeri" bir deneyim sağlayan jQuery File Download oluşturdum . Eklentinin çözdüğü ortak soruyla ilgili blog yazımıza bir göz atın ve bunu kullanmanın bazı yolları ve ayrıca jQuery Dosya İndirme eyleminin bir demosu . İşte kaynak

İşte sözler ile eklenti kaynağını kullanarak basit bir kullanım örneği demo . Demo sayfası sıra diğer birçok, 'daha iyi UX' örneklerini içerir.

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

Hangi tarayıcıları desteklemeniz gerektiğine bağlı olarak, jQuery Dosya İndirme'nin kullandığı IFRAME yönteminden daha açık kontrole izin veren https://github.com/eligrey/FileSaver.js/ adresini kullanabilirsiniz .


69
Yaptığınız şeyi seviyorum ama daha fazla StackOverFlow kredisi almak için buradaki cevabınızın biraz daha fazla ayrıntı içermesi gerektiğinden şüpheleniyorum. Özellikle sorunu nasıl çözdüğünüz üzerine.
AnthonyVO

14
bizi görmek için blog / eklenti kaynağına gitmeye zorlamak yerine, bu "eklentinin" nasıl sınırlandırıldığını tam olarak anlarsanız iyi olur. örneğin, bunun yerine bir iframe yayınlıyor mu? bunun yerine uzak komut dosyasının dosyayı kaydetmesini ve bir URL göndermesini gerektiriyor mu?
Kevin B

2
@asgerhallas Elbette, ancak söz konusu bağlantı koparsa bu tamamen işe yaramaz.
Kevin B

26
Kabul ediyorum, bir blog eklentinizi nasıl kullanacağınıza ve nasıl çalıştığına dair uzun bir açıklama yapmak için çok daha iyi bir yer. ancak en azından bu eklentinin sorunu nasıl çözdüğü hakkında kısa bir genel bakış sunmuş olabilirsiniz. Örneğin, bu, sunucunun bir çerez ayarlamasını ve javascript'inizin var olana kadar sürekli çerez aramasını sağlayarak sorunu çözer. Bir kez var olduğunda, indirme işleminin tamamlandığını varsayabiliriz. Bu tür bilgilerle kendi çözümlerini çok hızlı bir şekilde yuvarlayabilir ve yanıt artık% 100 blog / eklenti / jquery'nize dayanmaz ve diğer kütüphanelere uygulanabilir.
Kevin B

1
Royi, anladığım kadarıyla AJAX, diske kaydetmek için bir dosya indirme açılır penceresi ile sonuçlanan dosya indirmelerini asla destekleyemez. Farkında olmadığım bir yol buldun mu?
John Culviner

227

Kimse bu @ Pekka'nın çözümünü gönderdi ... ben de göndereceğim. Birine yardım edebilir.

Bunu Ajax aracılığıyla yapmanıza gerek yoktur. Sadece kullan

window.location="download.action?para1=value1...."

4
Güzel bir ... Ben indirme dosyası istemi işleme ve jquery ajax kullanarak mücadele gibi ... ve bu çözüm benim için mükemmel çalışıyor .. + 1
swapnesh

45
Bunun, sunucunun 'ekin' İçerik-Eğim üstbilgi değerini ayarlamasını gerektirdiğini unutmayın, aksi takdirde tarayıcı yanıt içeriğine yönlendirir (ve görüntüler)
brichins

21
Alternatif olarak window.open(<url>, '_blank');, indirmenin mevcut tarayıcı içeriğinizin (Content-Disposition başlığından bağımsız olarak) yerini almamasını sağlamak için kullanın .
Christopher King

4
Bu çözümdeki sorun, işlem başarısız olursa / sunucu bir hata döndürürse, sayfanızın hata sayfasına yönlendirilmesidir. Bunu çözmek için iFrame çözümünü kullanın
kofifus

4
Bu çözümle ilgili asıl sorun soru ile ilgilidir POST.
Atomosk

35

HTML5 ile yapabilirsiniz

Not: İkili verileri JSON kodlayamadığından, döndürülen dosya verileri base64 kodlu olmalıdır ZORUNLU

Cevabımda şöyle AJAXbir veri yapısı var:

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

Bu, AJAX aracılığıyla bir dosyayı kaydetmek için aşağıdakileri yapabileceğim anlamına gelir

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

Fonksiyon base64ToBlob alındı burada ve bu işlev uygun olarak kullanılmalıdır

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

Sunucunuz kaydedilecek dosya verilerini boşaltıyorsa bu iyidir. Ancak, bir HTML4 yedekini nasıl uygulayacağımı tam olarak çözemedim


1
a.click()... içinde ateş çalışmalarına konusunda fikrin görünmüyor?
bigpony

Bazı tarayıcılarda abu kodun çalışması ve / veya revokeObjectURLparçanın kaldırılması için document.body.appendChild(a)
dom'a eklemeniz gerekebilir

günümü kurtardı (ve muhtemelen bir iş de :)) Herhangi bir önlemle bir javascript uzmanı ... daha fazla java guy. Ancak, neden basit bir "createObjectURL (yeni Blob ([atob (base64)]))" çalışmaz hakkında hiçbir fikrim yok! Tüm içgüdü bunu yapmak zorundayken bunu söylemez. grrr ...
apil.tamang

hatta var bytechars = atob(base64)hata veriyor JavaScript runtime error: InvalidCharacterError. Chrome Sürüm 75.0.3770.142 kullanıyorum, ancak burada yanlış olan ne olduğunu bilmiyorum.
Muflix

27

1. Çerçeve agnostik: Servlet indirme dosyası ek olarak

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

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

2. Struts2 Framework: Dosyayı eklenti olarak indirme işlemi

<!-- 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>Etiketi ile oluşturulan bir URL'ye OGNL ile işaret eden etiketi kullanmak daha iyi olur :<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>

Yukarıdaki durumlarda, dosyanın indirilmesi ( ) ve tarayıcı tarafından açılmaması ( ) gerektiğini belirterek , yanıta Content-Disposition üstbilgisini yazmanız gerekir . Sen ihtiyaç belirtmek için İçerik Türüattachmentinline de ve (yardım için gerçekçi bir progressbar çizim tarayıcı) dosya adı ve uzunluğu eklemek isteyebilir.

Örneğin, bir ZIP indirirken:

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 ile (Eylem'i bir Servlet, örneğin doğrudan akış için bir saldırı olarak kullanmıyorsanız ), yanıta doğrudan bir şey yazmanız gerekmez; yalnızca Stream sonuç türünü kullanarak ve struts.xml dosyasında yapılandırdığınızda çalışır: EXAMPLE

<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. Çerçeve agnostik (/ Struts2 çerçevesi): Tarayıcı içindeki Servlet (/ Eylem) açılış dosyası

Dosyayı indirmek yerine tarayıcının içinde açmak istiyorsanız, İçerik düzenleme satır içi olarak ayarlanmalıdır , ancak hedef geçerli pencere konumu olamaz; javascript tarafından oluşturulan yeni bir pencereyi <iframe>, sayfada veya yeni bir pencereyi "tartışılan" target = "_ blank" ile anında hedeflemeniz gerekir:

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

2
Efendim,
girdiniz

1
"Window.open" den bahseden tek cevap budur (yorumlardan biri bundan bahseder).
Andrew Koster

Çok fazla parametreniz varsa çalışmaz, çünkü too long urlhata alırsınız .
Muflix

25

Tarayıcının bir dosya indirmesini sağlamanın basit yolu, isteği şu şekilde yapmaktır:

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

Bu, tarayıcı indirme açılır penceresini açar.


3
Teşekkürler, bu çözümü kullandım. Bir cazibe gibi çalıştı. Ayrıca, yanıttan bir blob almazsanız, sadece yeni bir Blob oluşturun.
fabio.sang

6

IE11 ile çalışıyorsanız @startsWith_R bağlantısı gerçekten yardımcı oluyor
alexventuraio

Teşekkürler benim için çalıştı!
Zaki Muhammed

23

Geçici çözüm olarak küçük bir işlev oluşturdum (@JohnCulviner eklentisinden esinlenerek):

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

Tıklama etkinliğiyle demo:

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

Bu verileri çok garip bir şekilde sunucuya gönderir . Acaba uyumlu bir POST oluşturmak için değiştirilip değiştirilemeyeceğini?
Shayne

16

Aynı sorunla karşılaştım ve başarıyla çözdüm. Benim kullanım durumum bu.

" JSON verilerini sunucuya gönderin ve bir excel dosyası alın. Bu excel dosyası sunucu tarafından oluşturulur ve istemciye yanıt olarak döndürülür. Bu yanıtı tarayıcıda özel ada sahip bir dosya olarak indirin "

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

Yukarıdaki pasaj sadece aşağıdakileri yapıyor

  • XMLHttpRequest kullanarak bir diziyi sunucuya JSON olarak gönderme.
  • İçeriği bir blob (ikili) olarak getirdikten sonra, indirilebilir bir URL oluşturuyoruz ve onu görünmez "a" bağlantısına ekleyip tıklıyoruz. Burada bir POST isteği yaptım. Bunun yerine, basit bir GET için de gidebilirsiniz. Ajax aracılığıyla dosyayı indiremiyoruz, XMLHttpRequest kullanmalısınız.

Burada sunucu tarafında dikkatlice birkaç şey ayarlamamız gerekiyor. Python Django HttpResponse'de birkaç başlık ayarladım. Başka programlama dilleri kullanıyorsanız bunları uygun şekilde ayarlamanız gerekir.

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

Burada xls (excel) indirdiğim için contentType'ı bir tanesinin üstüne ayarladım. Dosya türünüze göre ayarlamanız gerekir. Bu tekniği her türlü dosyayı indirmek için kullanabilirsiniz.


Msgstr "Dosyayı Ajax üzerinden indiremiyoruz, XMLHttpRequest kullanmalıyız". XMLHttpRequest tanımı gereği AJAX'tır. Aksi takdirde modern web tarayıcıları için harika bir çözüm. Desteklemeyen IE için, HTMLAnchorElement.downloadözel msSaveOrOpenBlob yöntemiyle birleştirmeyi düşünüyorum .
Tsahi Asher

15

Tamam, ndpu kod heres dayalı ajax_download gelişmiş (sanırım) sürümü; -

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

Bunu şu şekilde kullanın;

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

Parametreler, önceki örneğe göre bir json kodlu dize yerine bir girdiden geliyormuş gibi uygun post params olarak gönderilir.

CAVEAT: Bu formlara değişken enjeksiyon potansiyeli konusunda dikkatli olun. Bu değişkenleri kodlamanın daha güvenli bir yolu olabilir. Alternatif olarak onlardan kaçmayı düşünün.


Bu çalışan bir örnektir. Teşekkürler. Bunu iframe olmadan, ancak window.location olmadan yapmak mümkün mü?
Marek Bar

Sanırım gizli formu DOM'un altına ekleyebilirsin. Ayrıca, eski tarayıcılarda mutlaka iyi desteklenmese de, keşfetmeye değer Shadow dom kullanımıdır.
Shayne

Bu kodda bu hatayı alıyorum. 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.
geçersiz

Bu formu bazı model sınıflarıyla nasıl eşleyebilirim? Ben var: @ResourceMapping() public void downloadFile(final ResourceRequest request, final ResourceResponse response, @ModelAttribute("downForm") FormModel model) ama çalışmıyor ..
bartex9

void: Bu muhtemelen bir tür çapraz kökenli güvenlik sorunu olurdu. Muhtemelen bütün bir yığın taşma sorusu kendinde ve kendinde. @ bartex9: Bu, ne tür bir çerçeve kullandığınıza bağlı olacaktır. Ancak ilke, dosyanın kendisini web sisteminin erişilebilir bir alanına veya yüksek kullanılabilirlik için amazon S3 gibi bir şeye iterken adı ve yolu alıp saklamak olacaktır
Shayne

8

İşte yaptığım şey, saf javascript ve html. Test etmedi, ancak bu tüm tarayıcılarda çalışmalıdır.

Javascript İşlevi

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

Yalnızca tüm tarayıcılarda desteklenen bileşenleri kullanarak ek kitaplık kullanmayın.

resim açıklamasını buraya girin resim açıklamasını buraya girin

İşte sunucu tarafı JAVA Spring kontrolör kodu.

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

yükleme etkinliğinizin İçerik düzenleme eki içeriği için (iframe'e hiçbir şey yüklenmediğinden) çağrılmadığı anlaşılıyor, eğer sizin için çalışıyorsa (console.log'u alırsınız) lütfen bir örnek gönderin
kofifus

İşte hızlı bir keman jsfiddle.net/y2xezyoj bu pdf dosyası iframe içine yüklenir yüklenmez ass olayı ateşler .. bu keman indirmek için indirmez anahtar sunucu tarafında "response.setHeader (" İçerik -disposition "," ek ; dosyaadı = \ "" + dosyaAdı + ".xlsx \" ");"
manukyanv07

1
evet bu durumda çalışacaktır, ancak dosya indirilirse, sunucu Content-Disposition: attachment gönderir, o zaman load olayı
tetiklemez

Sunucu tamamlandıktan hemen sonra işleme yükleme dosya göndermeye başlar tamamen doğru yükleme olayı tetiklenir. Aradığım şey budur, 1- düğmeyi bloke edin ve işlemlerin gösterilmesi, böylece kullanıcının bir şeyler olduğuna dair bir geri bildirim alabilirsiniz. 2 - Daha sonra sunucu işlemeyi bitirdiğinde ve düğmeyi açtığım ve işlem spinner 4'ü kaldırdığım dosya 3- (yükleme olayı tetiklendi) göndermek üzere olduğunda - kullanıcı şimdi kaydetme dosyası ile açılır veya tarayıcı indirmeye başlar tanımlı indirme konumu. Üzgünüm İngilizcem.
manukyanv07

5
function downloadURI(uri, name) 
{
    var link = document.createElement("a");
    link.download = name;
    link.href = uri;
    link.click();
}

Cevabınızı açıklayabilir misiniz? Bu, başkalarının yaptıklarınızı anlamalarına yardımcı olur, böylece tekniklerinizi durumlarına uygulayabilirler.
Wai Ha Lee

2
Sadece bir uyarı: Safari ve IE downloadözelliği desteklemediğinden, dosyanız "Bilinmeyen" adını alacak
Yangshun Tay

4

Rails'te bunu şu şekilde yaparım:

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

Hile window.location bölümüdür. Denetleyicinin yöntemi şuna benzer:

# GET /files/{:id}/download_file/
def download_file
    send_file(@file.file,
          :disposition => 'attachment',
          :url_based_filename => false)
end

2
Hızlı soru, bu dosyayı iki kez oluşturmayacak mı? Bir kez ajax isteği gönderin. Ardından sayfanın da aynı URL'ye yönlendirilmesini sağlarsınız. Bunu nasıl ortadan kaldırabiliriz?
coderhs

Benim durumumda değil. Yine de Chrome'da test ettim.
aarkerio

Kodlayıcılar zaten doğru şekilde belirtildiği için, eylem iki kez çağrılır.
Sven

Benim için de iki kez çağrılıyor.
CSquared

4

kullanım window.open Https://developer.mozilla.org/en-US/docs/Web/API/Window/open

Örneğin, bu kod satırını bir tıklama işleyicisine koyabilirsiniz:

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

Yeni bir sekme açar ('_blank' pencere adı nedeniyle) ve bu sekme URL'yi açar.

Sunucu tarafı kodunuzda şöyle bir şey olmalıdır:

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

Bu şekilde, tarayıcı kullanıcıdan yalnızca dosyayı göstermek yerine dosyayı diske kaydetmesini istemelidir. Ayrıca yeni açtığı sekmeyi otomatik olarak kapatır.


4

Bir CSV dosyası indirmeye ve indirme işlemi bittikten sonra bir şeyler yapmaya çalışıyorum. Bu yüzden uygun bir callbackişlevi yerine getirmem gerekiyor .

Kullanımı window.location="..."iyi bir fikir değil çünkü indirme işlemini bitirdikten sonra programı kullanamıyorum. Böyle bir şey, başlığı değiştirin, bu yüzden iyi bir fikir değildir.

fetchIE 11'i destekleyemese de iyi bir alternatiftir . Ve window.URL.createObjectURLIE 11.You başvurabilir destekleyemez bu .

Bu benim kodum, Shahrukh Alam'ın koduna benzer. Ama window.URL.createObjectURLbelki de bellek sızıntısı yaratmaya dikkat etmelisiniz . Sen başvurabilir bu . Yanıt geldiğinde, veriler tarayıcının belleğinde saklanır. aBağlantıyı tıklamadan önce dosya indirildi. Bu, indirdikten sonra her şeyi yapabileceğiniz anlamına gelir.

$.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)
}

4

AJAX ile bir dosyayı aldıktan sonra İNDİRME

Dosya uzun süre oluşturulduğunda ve PRELOADER'ı göstermeniz gerektiğinde kullanışlıdır

Bir web formu gönderirken örnek:

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

Opsiyonel fonksiyon, örneği basitleştirmek için yorumlanmıştır.

Sunucuda geçici dosyalar oluşturmaya gerek yoktur.

JQuery v2.2.4 üzerinde Tamam. Eski sürümde bir hata olacak:

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').

Content-Disposition'dan dosya adını almak için bu maç benim için çalıştı: filename.match(/filename=(.*)/)[1](çift tırnak işareti veya soru işareti olmadan) - regex101.com/r/2AsD4y/2 . Ancak, çözümünüz çok arama yaptıktan sonra işe yarayan tek çözümdü.
jstuardo

3

Bir dosyayı indirmek için yukarıdaki cevaba daha fazla şey eklemek

Aşağıda bayt dizisi üreten bazı java bahar kodu

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

Şimdi javascript kodu FileSaver.js kullanarak, aşağıdaki kodu içeren bir dosya indirebilirsiniz

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

Yukarıdaki dosya indirilecek


2

Tamam yani MVC kullanırken çalışma kodu ve dosyanızı bir denetleyiciden alıyorsunuz

Diyelim ki bayt diziniz bildirildi ve doldurun, yapmanız gereken tek şey Dosya işlevini kullanmaktır (System.Web.Mvc kullanarak)

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

ve sonra, aynı denetleyiciye, bu 2 işlevi ekleyin

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

ve sonra "başarılı" veya "başarısız" geri aramasını indirmek ve almak için kumandanızı arayabileceksiniz.

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

            },
            failCallback: function (html, url) {
            //insert fail code
            }
        });

1

Aslında ajax kullanmadığı halde, indirmeyi istemek için bir javascript çağrısı kullanmanıza ve indirme işlemi gerçekten başladığında geri arama almanıza izin veren bir düzeltme buldum. Bağlantı göndermeden önce dosyayı oluşturmak için biraz alır bir sunucu tarafı komut dosyası çalıştırırsa bu yararlı buldum. böylece işlemek için onları uyarabilir ve daha sonra dosya gönderildiğinde o işleme bildirimini kaldırın. bu yüzden dosya istendiğinde ve gerçekten indirmeye başladığında başka bir olay olabilir böylece başlamak için ajax ile dosyayı yüklemek için denemek istedim.

ön sayfadaki js

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>

sonra diğer dosya:

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

Ben js kullanarak veri almak okumak için bir yol olduğunu düşünüyorum böylece hiçbir php gerekli olacaktır. ama elden bilmiyorum ve kullandığım sunucu php destekler, bu yüzden bu benim için çalışıyor. herkese yardım etmesi durumunda paylaşacağımı düşündüm.


0

JQuery Dosya İndirme'yi kullanmak istiyorsanız, lütfen IE için bunu not edin. Cevabı sıfırlamanız gerekiyor veya indirilmiyor

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

İşleminiz ServletResponseAware erişim için uygulanabilirgetServletResponse()


0

Ajax çağrısı ile yapamayacağınız kesindir.

Ancak, bir geçici çözüm vardır.

Adımlar:

Dosyayı indirmek için form.submit () kullanıyorsanız, yapabilecekleriniz:

  1. İstemciden sunucuya bir ajax çağrısı oluşturun ve dosya akışını oturum içinde saklayın.
  2. Sunucudan "başarılı" döndükten sonra, yalnızca oturumda depolanan dosya akışını akışlandırmak için form.submit () yöntemini çağırın.

Bu, form.submit () yaptıktan sonra dosyanın indirilip indirilmemesi gerektiğine karar vermek istediğinizde yararlıdır, örn: form.submit () öğesinde sunucu tarafında bir istisna oluşur ve bunun yerine çökme durumunda, istemci tarafında özel bir mesaj göstermeniz gerekebilir, bu durumda bu uygulama yardımcı olabilir.


0

ajax'ta bir web sayfası indirmek için başka bir çözüm daha var. Ama önce işlenip sonra indirilmesi gereken bir sayfaya atıfta bulunuyorum.

Öncelikle sayfa işlemeyi sonuç indirme işleminden ayırmanız gerekir.

1) Ajax çağrısında sadece sayfa hesaplamaları yapılır.

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

       işlev (veri, durum) 
       {
            if (status == "başarılı") 
            {
                / * 2) Cevapta önceki hesaplamaları kullanan sayfa indirilir. Örneğin, bu, ajax çağrısında hesaplanan bir tablonun sonuçlarını yazdıran bir sayfa olabilir. * /
                window.location.href = DownloadPage.php + "? ID =" + 29;
            }               
       }
);

// Örneğin: CalculusPage.php içinde

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

        $ query = "ÖrnekPage'E EKLE (data1, data2) DEĞERLER ('". $ _ POST ["data1"]. "', '". $ _ POST ["data2"]. "') NEREDE id =". $ ID;
        ...
    }

// Örneğin: DownloadPage.php içinde

    $ ID = $ _GET ["ID"];

    $ sede = "SELECT * FROM SamplePage WHERE id =". $ ID;
    ...

    $ Filename = "Export_Data.xls";
    başlık ("İçerik Tipi: application / vnd.ms-excel");
    header ("Content-Disposition: satır içi; dosyaadı = $ dosyaadı");

    ...

Umarım bu çözüm benim için olduğu gibi birçokları için de yararlı olabilir.


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

Yalnızca kod yanıtlarının en azından kodun nasıl çalıştığını ve soruyu neden yanıtladığını açıklayan bir minimum açıklaması olmalıdır.
Roberto Caboni

0

Bu herhangi bir tarayıcıda çok iyi çalışıyor (asp.net core kullanıyorum)

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

        }

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.