Ajax yazıdan dosya indirme işlemek


393

Belirli bir URL'ye ajax POST istekleri gönderen bir javascript uygulaması var. Yanıt bir JSON dizesi veya bir dosya (ek olarak) olabilir. Ajax çağrımda Content-Type ve Content-Disposition'ı kolayca algılayabilirim, ancak yanıtın bir dosya içerdiğini tespit ettikten sonra, istemciyi indirmesini nasıl öneririm? Ben burada benzer konuları bir dizi okudum ama hiçbiri aradığım cevap vermiyor.

Lütfen, lütfen, lütfen bunun için ajax kullanmamam gerektiğini veya tarayıcıyı yeniden yönlendirmem gerektiğini öneren cevaplar göndermeyin, çünkü bunların hiçbiri bir seçenek değildir. Düz bir HTML formu kullanmak da bir seçenek değildir. Ne gerek istemciye bir indirme iletişim kutusu göstermek için. Bu yapılabilir ve nasıl?


Bu makaleyi okuyanlar için, bu yazıyı okuyun: stackoverflow.com/questions/20830309/…
sobhan

Çözümünüzü sorudan kaldırdım. Aşağıdaki cevap postası olarak gönderebilirsiniz, ancak soru postasına ait değildir.
Martijn Pieters

Yanıtlar:


111

Bir form oluşturun, POST yöntemini kullanın, formu gönderin - iframe'e gerek yoktur. Sunucu sayfası isteğe yanıt verdiğinde, dosyanın mime türü için bir yanıt başlığı yazın ve bir indirme iletişim kutusu görüntüler - bunu birkaç kez yaptım.

İçerik türü uygulama / indirme istiyorsunuz - kullandığınız dil için nasıl indirme yapacağınızı araştırın.


35
Soruda belirtildiği gibi: "Düz bir HTML formu kullanmak da bir seçenek değildir."
Pavle Predic

13
Hayır, çünkü normal bir POST kullanmak tarayıcıyı POST URL'sine yönlendirir. Sayfadan uzaklaşmak istemiyorum. İsteği arka planda gerçekleştirmek, yanıtı işlemek ve istemciye sunmak istiyorum.
Pavle Predic

6
Sunucu, diğer yanıtın yaptığı gibi başlıkları geri gönderirse, yeni bir pencerede açılır - daha önce yaptım. Yalnızca sunucu tarafı komut dosyanız HTML kodu

1
@PavlePredic, her iki yanıt senaryosunu, yani JSON metin yanıtı veya indirme dosyası yanıtı nasıl yönetileceğini anladınız mı?
Web Kullanıcısı

9
Cevap net değil ve önerilen çözüm işe yaramıyor.
stack247

531

Çok hızlı vazgeçmeyin, çünkü bu FileAPI'nin bazı bölümleri kullanılarak yapılabilir (modern tarayıcılarda):

var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
    if (this.status === 200) {
        var filename = "";
        var disposition = xhr.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) {
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
        }
        var type = xhr.getResponseHeader('Content-Type');

        var blob;
        if (typeof File === 'function') {
            try {
                blob = new File([this.response], filename, { type: type });
            } catch (e) { /* Edge */ }
        }
        if (typeof blob === 'undefined') {
            blob = new Blob([this.response], { type: type });
        }

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) {
                // use HTML5 a[download] attribute to specify filename
                var a = document.createElement("a");
                // safari doesn't support this yet
                if (typeof a.download === 'undefined') {
                    window.location.href = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location.href = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
    }
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send($.param(params));

İşte jQuery.ajax kullanan eski sürüm. Yanıt, bazı karakter kümelerinden oluşan bir dizeye dönüştürüldüğünde ikili verileri değiştirebilir.

$.ajax({
    type: "POST",
    url: url,
    data: params,
    success: function(response, status, xhr) {
        // check for a filename
        var filename = "";
        var disposition = xhr.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) {
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
        }

        var type = xhr.getResponseHeader('Content-Type');
        var blob = new Blob([response], { type: type });

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) {
                // use HTML5 a[download] attribute to specify filename
                var a = document.createElement("a");
                // safari doesn't support this yet
                if (typeof a.download === 'undefined') {
                    window.location.href = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location.href = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
        }
    }
});

1
Çok teşekkürler ! HTTP yanıtımda çalışmasını sağlamak için hem 'Access-Control-Expose-Headers' hem de 'Access-Control-Allow-Headers' öğelerine 'İçerik-eğilim' eklemek zorunda kaldım.
JulienD

1
Dosya 500 mb'den büyük olduğunda çalışmıyor, belki başka bir API kullanmalıyız?
hirra

Temizleme kısmındaki (yalnızca URL'yi değil) bir öğeyi DOM'den kaldırmaya ne dersiniz? document.body.removeChild(a);
1919

@hirra, "arraybuffer" yerine responceType yazın ve onload geri çağırma işlevini bu şekilde yeniden yazın, bu var blob this.response (var blob = this.response;) yanivar blob =this.responce; /** if (typeof File === 'function') { try { blob = new File([this.response], filename, { type: type }); } catch (e) { /* Edge */ } } if (typeof blob === 'undefined') { blob = new Blob([this.response], { type: type }); } */
Chris Tobba

1
Bu mükemmel bir çözüm. Sadece küçük bir değişiklik. window.location.href = downloadUrlwindow.location = downloadUrl
Daktiloda

39

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


33

Hangi sunucu tarafı dili kullanıyorsunuz? Uygulamamda, PHP'nin yanıtında doğru başlıkları ayarlayarak AJAX çağrısından kolayca bir dosya indirebilirim:

Sunucu tarafı üstbilgilerini ayarlama

header("HTTP/1.1 200 OK");
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

// The optional second 'replace' parameter indicates whether the header
// should replace a previous similar header, or add a second header of
// the same type. By default it will replace, but if you pass in FALSE
// as the second argument you can force multiple headers of the same type.
header("Cache-Control: private", false);

header("Content-type: " . $mimeType);

// $strFileName is, of course, the filename of the file being downloaded. 
// This won't have to be the same name as the actual file.
header("Content-Disposition: attachment; filename=\"{$strFileName}\""); 

header("Content-Transfer-Encoding: binary");
header("Content-Length: " . mb_strlen($strFile));

// $strFile is a binary representation of the file that is being downloaded.
echo $strFile;

Bu aslında tarayıcıyı bu indirme sayfasına 'yönlendirecektir', ancak @ahren'in yorumunda söylediği gibi, geçerli sayfadan uzaklaşmayacak.

Her şey doğru başlıkları ayarlamakla ilgili, bu yüzden PHP değilse kullandığınız sunucu tarafı dili için uygun bir çözüm bulacağınızdan eminim.

Yanıt istemcisi tarafını işleme

Nasıl AJAX çağrısı yapacağınızı bildiğinizi varsayarsak, istemci tarafında sunucuya bir AJAX isteği yürütürsünüz. Sunucu daha sonra bu dosyanın indirilebileceği bir bağlantı oluşturur, örneğin, işaret etmek istediğiniz 'ileri' URL. Örneğin, sunucu şu yanıtı verir:

{
    status: 1, // ok
    // unique one-time download token, not required of course
    message: 'http://yourwebsite.com/getdownload/ska08912dsa'
}

Cevabı işlerken, iframevücudunuza bir enjekte edersiniz ve iframeSRC'sini bu şekilde aldığınız URL'ye ayarlarsınız (bu örneğin kolaylığı için jQuery kullanarak):

$("body").append("<iframe src='" + data.message +
  "' style='display: none;' ></iframe>");

Yukarıda gösterildiği gibi doğru başlıkları ayarladıysanız, iframe, tarayıcıyı geçerli sayfadan uzaklaştırmadan bir indirme iletişim kutusunu zorlar.

Not

Sorunuzla ilgili ekstra ekleme; AJAX teknolojisi ile bir şey isterken her zaman JSON dönmek en iyisi olduğunu düşünüyorum. JSON yanıtını aldıktan sonra, istemci tarafında bununla ne yapacağınıza karar verebilirsiniz. Örneğin, daha sonra kullanıcının indirmeyi doğrudan zorlamak yerine URL'ye bir indirme bağlantısını tıklamasını istersiniz, mevcut kurulumunuzda bunu yapmak için hem istemci hem de sunucu tarafını güncellemeniz gerekir.


24

Açısal bir perspektiften çözüm arayanlar için bu benim için çalıştı:

$http.post(
  'url',
  {},
  {responseType: 'arraybuffer'}
).then(function (response) {
  var headers = response.headers();
  var blob = new Blob([response.data],{type:headers['content-type']});
  var link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = "Filename";
  link.click();
});

Bu yardımcı oldu, ancak orijinal dosya adını korumam gerekiyor. Dosya adını "Content-Disposition" altındaki yanıt başlıklarında görüyorum, ancak bu değeri koddaki yanıt nesnesinde bulamıyorum. Ayarlama link.download = ""rastgele bir guid dosya adı ile link.download = nullsonuçlanır ve "null" adlı bir dosyayla sonuçlanır.
Marie

@ Dostum, INPUTöğenin HTMLInputElement.filesözelliğini kullanarak yükleme sırasında dosya adını kaydedebilirsiniz . Daha fazla ayrıntı için bkz. Dosya girişindeki MDN belgeleri .
Tim Hettler

Damla boyutu sınırlıdır: stackoverflow.com/questions/28307789/…
user0800

22

İşte bu şekilde çalıştım nasıl https://stackoverflow.com/a/27563953/2845977

$.ajax({
  url: '<URL_TO_FILE>',
  success: function(data) {
    var blob=new Blob([data]);
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="<FILENAME_TO_SAVE_WITH_EXTENSION>";
    link.click();
  }
});

Download.js kullanarak cevap güncellendi

$.ajax({
  url: '<URL_TO_FILE>',
  success: download.bind(true, "<FILENAME_TO_SAVE_WITH_EXTENSION>", "<FILE_MIME_TYPE>")
});


Bunun için teşekkürler, bunu bugün kullandım. Fantastik
Ryan Wilson

Merhaba, bunun çalışması için jQuery 3.0> olması gerekir mi?
gbade_

Ayrıca verdiğiniz her iki örnekle de boş bir pdf alıyorum. Bir pdf dosyası indirmek için almaya çalışıyorum.
gbade_

@gbade_ Hayır, herhangi bir özel jQuery sürümüne ihtiyacınız yok. Tüm sürümlerle iyi çalışmalıdır. İndirdiğiniz PDF'nin doğru CORS başlıklarına sahip olup olmadığını kontrol ettiniz mi? Konsoldaki herhangi bir hata ayıklamaya yardımcı olabilir
Mayur Padshala

Bu benim için download.js kullanarak çalıştı:success: function (response, status, request) { download(response, "filename.txt", "application/text"); }
Sga

12

Zaten bir çözüm bulduğunuzu görüyorum, ancak sadece büyük POST istekleriyle aynı şeyi başarmaya çalışan birine yardımcı olabilecek bazı bilgiler eklemek istedim.

Birkaç hafta önce aynı sorunu yaşadım, gerçekten AJAX aracılığıyla "temiz" bir indirme elde etmek mümkün değil, Filament Grubu tam olarak nasıl bulduğunuzu çalıştıran bir jQuery eklentisi oluşturdu, buna jQuery Dosyası denir Ancak indir bu tekniğin bir dezavantajı var.

AJAX aracılığıyla büyük talepler gönderiyorsanız (diyelim ki dosyalar + 1MB) yanıt vermeyi olumsuz yönde etkileyecektir. Yavaş İnternet bağlantılarında çok beklemek zorunda kalacaksınız , istek gönderilinceye kadar beklemeniz ve ayrıca dosyanın indirilmesini beklemeniz gerekir. Anlık bir "click" => "popup" => "indirme başlat" gibi değildir. Daha çok "click" => "veri gönderilinceye kadar bekle" => "yanıt bekle" => "indirme başlangıcı" gibi bir dosya var, çünkü isteğin gönderilmesini beklemek zorunda kalacaksınız. AJAX aracılığıyla indirin ve indirilebilir bir dosya olarak geri alın.

1 MB'tan küçük dosya boyutları ile çalışıyorsanız bunu fark etmeyeceksiniz. Ancak kendi uygulamamda keşfettiğim gibi, daha büyük dosya boyutları için neredeyse dayanılmaz.

Uygulamam, kullanıcıların dinamik olarak oluşturulan görüntüleri dışa aktarmasına izin veriyor, bu görüntüler POST istekleri aracılığıyla base64 biçiminde sunucuya gönderiliyor (tek olası yol), daha sonra işleniyor ve .png, .jpg dosyaları, base64 biçiminde kullanıcılara geri gönderiliyor + 1MB resimler için dizeler çok büyük, bu da kullanıcıları dosyanın indirilmeye başlaması için gereğinden fazla beklemeye zorluyor. Yavaş İnternet bağlantılarında gerçekten can sıkıcı olabilir.

Bunun benim çözümü, dosyayı sunucuya geçici olarak yazmaktı, hazır olduğunda, dinamik olarak "Lütfen bekleyin ..." ve "İndir" durumları arasında değişen bir düğme şeklinde dosyaya bir bağlantı oluşturmak ve aynı zamanda zaman, kullanıcıların "sağ tıklayıp" kaydedebilmesi için base64 görüntüsünü bir önizleme açılır penceresine yazdırın. Bu, tüm bekleme süresini kullanıcılar için daha katlanılabilir hale getirir ve ayrıca işleri hızlandırır.

30 Eylül 2014 Güncellemesi:

Bunu yayınladığımdan bu yana aylar geçti, sonunda büyük base64 dizeleriyle çalışırken işleri hızlandırmak için daha iyi bir yaklaşım buldum. Şimdi veritabanına base64 dizeleri depolamak (longtext veya longblog alanları kullanarak), daha sonra jQuery File Download üzerinden kayıt kimliğini geçiyorum, nihayet karşıdan yükleme komut dosyası üzerinde base64 dizesini çekmek ve geçmek için bu kimliği kullanarak veritabanını sorgulamak indirme işlevi.

Komut Dosyasını İndir Örnek:

<?php
// Record ID
$downloadID = (int)$_POST['id'];
// Query Data (this example uses CodeIgniter)
$data       = $CI->MyQueries->GetDownload( $downloadID );
// base64 tags are replaced by [removed], so we strip them out
$base64     = base64_decode( preg_replace('#\[removed\]#', '', $data[0]->image) );
// This example is for base64 images
$imgsize    = getimagesize( $base64 );
// Set content headers
header('Content-Disposition: attachment; filename="my-file.png"');
header('Content-type: '.$imgsize['mime']);
// Force download
echo $base64;
?>

Bunun OP'nin istediklerinin çok ötesinde olduğunu biliyorum, ancak cevabımı bulgularımla güncellemenin iyi olacağını hissettim. Sorunum için çözümler ararken , aradığım yanıtı vermeyen çok sayıda "AJAX POST verilerinden indir" konuları okudum , umarım bu bilgiler böyle bir şeye ulaşmak isteyen birine yardımcı olur.


jQuery File DownloadSadece url beni yönlendirir. Böyle diyoruz: jQuery.download("api/ide/download-this-file.php", {filePath: path2Down}, "POST");.
Casper

5

Kabul edilen cevapta tekniği kullanırken ortaya çıkan bazı zorlukları, yani bir form postasını kullanarak belirtmek istiyorum:

  1. İstekte üstbilgi ayarlayamazsınız. Kimlik doğrulama şemanız, Yetkilendirme başlığından geçirilen bir Json-Web-Token başlıklarını içeriyorsa, bunu göndermek için başka bir yol bulmanız gerekir, örneğin bir sorgu parametresi.

  2. İsteğin ne zaman bittiğini gerçekten söyleyemezsiniz. Peki, jquery.fileDownload tarafından yapılan yanıt olarak ayarlanan bir çerez kullanabilirsiniz , ancak mükemmel FAR. Eşzamanlı istekler için çalışmaz ve bir yanıt asla gelmezse kırılır.

  3. Sunucu bir hatayla yanıt verirse, kullanıcı hata sayfasına yönlendirilir.

  4. Yalnızca bir form tarafından desteklenen içerik türlerini kullanabilirsiniz . Yani JSON kullanamazsınız.

Dosyayı S3'e kaydetme ve dosyayı almak için önceden imzalanmış bir URL gönderme yöntemini kullanarak bitirdim.


5

Daha modern bir yaklaşım arayanlar için fetch API. Aşağıdaki örnekte bir elektronik tablo dosyasının nasıl indirileceği gösterilmektedir. Aşağıdaki kod ile kolayca yapılabilir.

fetch(url, {
    body: JSON.stringify(data),
    method: 'POST',
    headers: {
        'Content-Type': 'application/json; charset=utf-8'
    },
})
.then(response => response.blob())
.then(response => {
    const blob = new Blob([response], {type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
    const downloadUrl = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = downloadUrl;
    a.download = "file.xlsx";
    document.body.appendChild(a);
    a.click();
})

Bu yaklaşımın anlaşılması diğer XMLHttpRequestçözümlere göre çok daha kolay olduğuna inanıyorum . Ayrıca, jQueryek kitaplık eklemeye gerek kalmadan, yaklaşımla benzer bir sözdizimine sahiptir .

Tabii ki, bu yeni yaklaşım IE üzerinde çalışmadığından hangi tarayıcıyı geliştirdiğinizi kontrol etmenizi tavsiye ederim. Tam tarayıcı uyumluluk listesini aşağıdaki [bağlantı] [1] 'de bulabilirsiniz.

Önemli : Bu örnekte verilen bir sunucuyu dinleyen bir JSON isteği gönderiyorum url. Bu urlayarlanmalıdır, benim örneğimde bu kısmı bildiğinizi varsayıyorum. Ayrıca, isteğinizin çalışması için gereken başlıkları da göz önünde bulundurun. Bir JSON gönderdiğim Content-Typeiçin application/json; charset=utf-8, sunucunun alacağı istek türünü bilmesini sağlamak için üstbilgiyi eklemeli ve ayarlamalıyım .


1
Müthiş! Bunu indirme açılır penceresi yerine yeni bir sekmede açmak için: `` const window = open (downloadUrl, "_blank"); if (window! == null) window.focus (); ``
andy

Bunu birden fazla veri kümesi için yapmamın bir yolu var mı? Örnek, api çağrısından {fileOne: data, fileTwo: data, fileThree: data} dosyasını geri alın ve aynı anda indirilen üç dosya mı oluşturuyorsunuz? Teşekkürler!
il0v3d0g

Hmmm, bunu denemedim. Ancak görüntüleri her zaman bir zip dosyasında sıkıştırabilir ve indirebilirsiniz. Mümkün olup olmadığını kontrol edeceğim.
Alain Cruz

4

Geçici bir gizli form kullanarak çözümüm.

//Create an hidden form
var form = $('<form>', {'method': 'POST', 'action': this.href}).hide();

//Add params
var params = { ...your params... };
$.each(params, function (k, v) {
    form.append($('<input>', {'type': 'hidden', 'name': k, 'value': v}));
});

//Make it part of the document and submit
$('body').append(form);
form.submit();

//Clean up
form.remove();

Ben büyük ölçüde JQuery kullandığınızı ancak aynı yerel JS ile yapabilirsiniz unutmayın.


3

Diğerlerinin de belirttiği gibi, POST isteği ile indirmek için bir form oluşturabilir ve gönderebilirsiniz. Ancak, bunu manuel olarak yapmanız gerekmez.

Tam olarak bunu yapmak için gerçekten basit bir kütüphane jquery.redirect . Standart jQuery.postyönteme benzer bir API sağlar :

$.redirect(url, [values, [method, [target]]])

3

Bu 3 yaşında bir soru ama bugün de aynı sorunu yaşadım. Düzenlenmiş çözümünüze baktım, ancak performanstan ödün verebileceğini düşünüyorum çünkü ikili bir istekte bulunmalı. Herkes hizmet iki kez çağırmak anlamına gelmez başka bir çözüm gerekiyorsa, bu yüzden bunu yaptım yolu:

<form id="export-csv-form" method="POST" action="/the/path/to/file">
    <input type="hidden" name="anyValueToPassTheServer" value="">
</form>

Bu form sadece hizmeti çağırmak ve bir window.location () kullanmaktan kaçınmak için kullanılır. Bundan sonra, hizmeti aramak ve dosyayı almak için jquery'den bir form göndermeniz yeterlidir. Oldukça basit ama bu şekilde bir POST kullanarak indirme yapabilirsiniz . Şimdi, aradığınız hizmet bir GET ise, bu daha kolay olabilir , ama bu benim durumum değil.


1
Soru ajax kullanıyor olduğu için bu bir ajax yazısı değil
Nidhin David

ajax çağrıları için değil, yukarıdaki soruna sadece bir çözümdür.
Nomi Ali

1

Bu FileSaver.js'yi kullandım . Benim durumumda csv dosyaları, ben bunu (coffescript):

  $.ajax
    url: "url-to-server"
    data: "data-to-send"
    success: (csvData)->
      blob = new Blob([csvData], { type: 'text/csv' })
      saveAs(blob, "filename.csv")

En karmaşık vaka için, verilerin düzgün bir şekilde işlenmesi gerektiğini düşünüyorum. Başlık altında FileSaver.js, Jonathan Amend'in cevabı ile aynı yaklaşımı uygulayın .


1
..ama genellikle iOS'ta dosya indirebilir misiniz?
Alex Marshall


1

Almak için Jonathan Değiştirdi cevabı Edge çalışmalarına aşağıdaki değişiklikleri yaptı:

var blob = typeof File === 'function'
    ? new File([this.response], filename, { type: type })
    : new Blob([this.response], { type: type });

buna

var f = typeof File+"";
var blob = f === 'function' && Modernizr.fileapi
    ? new File([this.response], filename, { type: type })
    : new Blob([this.response], { type: type });

Bunu bir yorum olarak yayınlamayı tercih ederim ama bunun için yeterli itibarım yok


0

İşte benim çözüm, farklı kaynaklardan toplanan: Sunucu tarafı uygulama:

    String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
    // Set headers
    response.setHeader("content-disposition", "attachment; filename =" + fileName);
    response.setContentType(contentType);
    // Copy file to output stream
    ServletOutputStream servletOutputStream = response.getOutputStream();
    try (InputStream inputStream = new FileInputStream(file)) {
        IOUtils.copy(inputStream, servletOutputStream);
    } finally {
        servletOutputStream.flush();
        Utils.closeQuitely(servletOutputStream);
        fileToDownload = null;
    }

İstemci tarafı uygulaması (jquery kullanarak):

$.ajax({
type: 'POST',
contentType: 'application/json',
    url: <download file url>,
    data: JSON.stringify(postObject),
    error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert(errorThrown);
    },
    success: function(message, textStatus, response) {
       var header = response.getResponseHeader('Content-Disposition');
       var fileName = header.split("=")[1];
       var blob = new Blob([message]);
       var link = document.createElement('a');
       link.href = window.URL.createObjectURL(blob);
       link.download = fileName;
       link.click();
    }
});   

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

Yanıt bir Dizi Arabelleği ise , Ajax'taki başarılı olma olayında bunu deneyin:

 if (event.data instanceof ArrayBuffer) {
          var binary = '';
          var bytes = new Uint8Array(event.data);
          for (var i = 0; i < bytes.byteLength; i++) {
              binary += String.fromCharCode(bytes[i])
          }
          $("#some_id").append("<li><img src=\"data:image/png;base64," + window.btoa(binary) + "\"/></span></li>");
          return;
      }
  • Burada event.data, xhr olayının başarı işlevinde alınan yanıttır.

0

Aşağıda, bazı kimliklerden oluşan ve veritabanında arama yapan bazı listelere bağlı olarak birden fazla dosyayı indirmeye yönelik çözümüm, dosyalar belirlenecek ve indirilmeye hazır olacak - eğer varsa. Ajax kullanarak her dosya için C # MVC eylem çağırıyorum.

Ve evet, diğerlerinin söylediği gibi, bunu jQuery Ajax'ta yapmak mümkündür. Ajax'ın başarısıyla yaptım ve her zaman 200 yanıtı gönderiyorum.

Yani, bu anahtar:

  success: function (data, textStatus, xhr) {

Ve bu benim kodum:

var i = 0;
var max = 0;
function DownloadMultipleFiles() {
            if ($(".dataTables_scrollBody>tr.selected").length > 0) {
                var list = [];
                showPreloader();
                $(".dataTables_scrollBody>tr.selected").each(function (e) {
                    var element = $(this);
                    var orderid = element.data("orderid");
                    var iscustom = element.data("iscustom");
                    var orderlineid = element.data("orderlineid");
                    var folderPath = "";
                    var fileName = "";

                    list.push({ orderId: orderid, isCustomOrderLine: iscustom, orderLineId: orderlineid, folderPath: folderPath, fileName: fileName });
                });
                i = 0;
                max = list.length;
                DownloadFile(list);
            }
        }

Ardından:

function DownloadFile(list) {
        $.ajax({
            url: '@Url.Action("OpenFile","OrderLines")',
            type: "post",
            data: list[i],
            xhrFields: {
                responseType: 'blob'
            },
            beforeSend: function (xhr) {
                xhr.setRequestHeader("RequestVerificationToken",
                    $('input:hidden[name="__RequestVerificationToken"]').val());

            },
            success: function (data, textStatus, xhr) {
                // check for a filename
                var filename = "";
                var disposition = xhr.getResponseHeader('Content-Disposition');
                if (disposition && disposition.indexOf('attachment') !== -1) {
                    var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                    var matches = filenameRegex.exec(disposition);
                    if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
                    var a = document.createElement('a');
                    var url = window.URL.createObjectURL(data);
                    a.href = url;
                    a.download = filename;
                    document.body.append(a);
                    a.click();
                    a.remove();
                    window.URL.revokeObjectURL(url);
                }
                else {
                    getErrorToastMessage("Production file for order line " + list[i].orderLineId + " does not exist");
                }
                i = i + 1;
                if (i < max) {
                    DownloadFile(list);
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {

            },
            complete: function () {
                if(i===max)
                hidePreloader();
            }
        });
    }

C # MVC:

 [HttpPost]
 [ValidateAntiForgeryToken]
public IActionResult OpenFile(OrderLineSimpleModel model)
        {
            byte[] file = null;

            try
            {
                if (model != null)
                {
                    //code for getting file from api - part is missing here as not important for this example
                    file = apiHandler.Get<byte[]>(downloadApiUrl, token);

                    var contentDispositionHeader = new System.Net.Mime.ContentDisposition
                    {
                        Inline = true,
                        FileName = fileName
                    };
                    //    Response.Headers.Add("Content-Disposition", contentDispositionHeader.ToString() + "; attachment");
                    Response.Headers.Add("Content-Type", "application/pdf");
                    Response.Headers.Add("Content-Disposition", "attachment; filename=" + fileName);
                    Response.Headers.Add("Content-Transfer-Encoding", "binary");
                    Response.Headers.Add("Content-Length", file.Length.ToString());

                }
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, "Error getting pdf", null);
                return Ok();
            }

            return File(file, System.Net.Mime.MediaTypeNames.Application.Pdf);
        }

200 yanıtını döndürdüğünüz sürece, Ajax'taki başarı onunla çalışabilir, bu durumda aşağıdaki satırın yanlış olup olmadığını ve gerçekten bu konuda kullanıcıyı bilgilendirebilirsiniz:

 if (disposition && disposition.indexOf('attachment') !== -1) {

0

@ Alain-cruz'ın birine benzer bir çözüm gerekiyordu, ancak birden fazla indirme ile nuxt / vue. Tarayıcıların birden fazla dosya indirmesini engellediğini biliyorum ve ayrıca bir csv formatlı veri kümesi döndüren API'ye sahibim.İlk önce JSZip kullanacaktım ama burada IE desteğine ihtiyacım vardı. Birisi bana bunu iyileştirmek için yardımcı olabilirse, bu harika olurdu, ama şu ana kadar benim için çalışıyor.

API döndürür:

data : {
  body: {
    fileOne: ""col1", "col2", "datarow1.1", "datarow1.2"...so on",
    fileTwo: ""col1", "col2"..."
  }
}

page.vue:

<template>
  <b-link @click.prevent="handleFileExport">Export<b-link>
</template>

export default = {
   data() {
     return {
       fileNames: ['fileOne', 'fileTwo'],
     }
   },
  computed: {
    ...mapState({
       fileOne: (state) => state.exportFile.fileOne,
       fileTwo: (state) => state.exportFile.fileTwo,
    }),
  },
  method: {
    handleExport() {
      //exportFileAction in store/exportFile needs to return promise
      this.$store.dispatch('exportFile/exportFileAction', paramsToSend)
        .then(async (response) => {
           const downloadPrep = this.fileNames.map(async (fileName) => {
           // using lodash to get computed data by the file name
           const currentData = await _.get(this, `${fileName}`);
           const currentFileName = fileName;
           return { currentData, currentFileName };
         });
         const response = await Promise.all(downloadPrep);
         return response;
       })
       .then(async (data) => {
         data.forEach(({ currentData, currentFileName }) => {
           this.forceFileDownload(currentData, currentFileName);
         });
       })
       .catch(console.error);
    },
    forceFileDownload(data, fileName) {
     const url = window.URL
         .createObjectURL(new Blob([data], { type: 'text/csv;charset=utf-8;' }));
     const link = document.createElement('a');
     link.href = url;
     link.setAttribute('download', `${fileName}.csv`);
     document.body.appendChild(link);
     link.click();
   },
}
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.