अजाक्स पोस्ट से हैंडल फ़ाइल डाउनलोड करें


392

मेरे पास एक जावास्क्रिप्ट ऐप है जो एक निश्चित URL के लिए ajax POST अनुरोध भेजता है। प्रतिक्रिया एक JSON स्ट्रिंग हो सकती है या यह एक फ़ाइल (अनुलग्नक के रूप में) हो सकती है। मैं अपने ajax कॉल में सामग्री-प्रकार और सामग्री-विवाद का आसानी से पता लगा सकता हूं, लेकिन एक बार जब मुझे पता चलता है कि प्रतिक्रिया में फ़ाइल है, तो मैं इसे डाउनलोड करने के लिए क्लाइंट को कैसे ऑफ़र करूं? मैंने यहाँ इसी तरह के कई सूत्र पढ़े हैं, लेकिन उनमें से कोई भी उत्तर नहीं दिया है जिसकी मुझे तलाश है।

कृपया, कृपया, कृपया जवाब देने का सुझाव न दें कि मुझे इसके लिए ajax का उपयोग नहीं करना चाहिए या यह कि मुझे ब्राउज़र को पुनर्निर्देशित करना चाहिए, क्योंकि इसमें से कोई भी विकल्प नहीं है। सादे HTML फॉर्म का उपयोग करना भी एक विकल्प नहीं है। क्लाइंट को डाउनलोड डायलॉग दिखाने के लिए मुझे क्या चाहिए। क्या ऐसा किया जा सकता है और कैसे?


इस लेख को पढ़ने वालों के लिए इस पोस्ट को पढ़ें: stackoverflow.com/questions/20830309/…
sobhan

मैंने प्रश्न से आपका समाधान निकाल दिया है। आप इसे उत्तर पोस्ट के रूप में नीचे पोस्ट करने के लिए स्वागत कर रहे हैं, लेकिन यह प्रश्न पोस्ट में नहीं है।
मार्टिन पीटर्स

जवाबों:


111

एक फ़ॉर्म बनाएँ, POST विधि का उपयोग करें, फ़ॉर्म सबमिट करें - iframe की कोई आवश्यकता नहीं है। जब सर्वर पृष्ठ अनुरोध का जवाब देता है, तो फ़ाइल के माइम प्रकार के लिए एक प्रतिक्रिया शीर्ष लेख लिखें, और यह एक डाउनलोड संवाद पेश करेगा - मैंने इसे कई बार किया है।

आप एप्लिकेशन / डाउनलोड की सामग्री-प्रकार चाहते हैं - जो भी भाषा आप उपयोग कर रहे हैं उसके लिए एक डाउनलोड प्रदान करने का तरीका खोजें।


35
जैसा कि प्रश्न में कहा गया है: "एक सादे HTML फॉर्म का उपयोग करना भी एक विकल्प नहीं है।"
पावेल प्रेडिक

13
नहीं, क्योंकि नियमित POST का उपयोग करने से ब्राउज़र POST URL पर नेविगेट हो जाएगा। मैं पेज से दूर नहीं जाना चाहता। मैं बैकग्राउंड में रिक्वेस्ट करना चाहता हूं, रिस्पॉन्स प्रोसेस करना और क्लाइंट के सामने पेश करना चाहता हूं।
पावेल प्रेडिक

6
यदि सर्वर वापस हेडर भेजता है जैसे अन्य उत्तर में है, यह एक नई विंडो में खुलता है - मैंने इसे पहले किया है। यदि आपका सर्वर-साइड स्क्रिप्ट HTML कोड

1
@PavlePredic क्या आपने पता लगाया कि दोनों प्रतिक्रिया परिदृश्यों को कैसे प्रबंधित किया जाए, यानी JSON टेक्स्ट रिस्पांस या डाउनलोड फ़ाइल प्रतिक्रिया?
वेब यूजर

9
उत्तर स्पष्ट नहीं है और प्रस्तावित समाधान काम नहीं करता है।
स्टैक 247

530

इतनी जल्दी मत छोड़ो, क्योंकि यह (आधुनिक ब्राउज़रों में) FileAPI के कुछ हिस्सों का उपयोग करके किया जा सकता है:

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

यहाँ jQuery.ajax का उपयोग करके पुराना संस्करण है। जब यह प्रतिक्रिया कुछ चार्ट के स्ट्रिंग में परिवर्तित हो जाती है, तो यह बाइनरी डेटा को मनल कर सकता है।

$.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
आपका बहुत बहुत धन्यवाद ! मुझे अपने HTTP प्रतिसाद में 'एक्सेस-कंट्रोल-एक्सपोज़-हेडर्स' और 'एक्सेस-कंट्रोल-कंट्रोल-एलाउंस-हेडर्स' में 'कंटेंट-डिस्पोजल' को जोड़ना पड़ा, हालांकि यह काम करने के लिए है।
जुलिएनड

1
यह तब काम नहीं करता है जब फ़ाइल 500 mb से बड़ी हो, हो सकता है कि हमें एक और एपीआई का उपयोग करना चाहिए?
हिरा

सफाई भाग में DOM से एक तत्व को हटाने के बारे में क्या (और सिर्फ URL नहीं)? document.body.removeChild(a);
स्कोरग्राफिक

@hirra उपयोग responceType बजाय "arraybuffer" और ऑनलोड कॉलबैक फ़ंक्शन कि जिस तरह से फिर से लिखने की "ब्लॉब", कि वर ब्लॉब this.response (वर ब्लॉब this.response =;) है, इसलिएvar 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 }); } */
क्रिस Tobba

1
यह अचूक उपाय है। बस एक छोटा सा बदलाव। टाइपप्रति में मैं जरूरत window.location.href = downloadUrlके बजायwindow.location = downloadUrl
michal.jakubeczy

39

मैंने उसी मुद्दे का सामना किया और इसे सफलतापूर्वक हल किया। मेरा उपयोग-मामला यह है।

" JSON डेटा को सर्वर पर पोस्ट करें और एक एक्सेल फाइल प्राप्त करें। वह एक्सेल फाइल सर्वर द्वारा बनाई गई है और क्लाइंट की प्रतिक्रिया के रूप में दी गई है। उस प्रतिक्रिया को ब्राउजर में कस्टम नाम वाली फाइल के रूप में डाउनलोड करें। "

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

उपरोक्त स्निपेट केवल निम्नलिखित कर रहा है

  • XMLHttpRequest का उपयोग करके सर्वर पर JSON के रूप में एक सरणी पोस्ट करना।
  • एक ब्लॉब (बाइनरी) के रूप में सामग्री लाने के बाद, हम एक डाउनलोड करने योग्य URL बना रहे हैं और इसे अदृश्य "लिंक" पर क्लिक करके इसे संलग्न कर रहे हैं।

यहां हमें सर्वर साइड पर कुछ चीजों को सावधानीपूर्वक सेट करने की आवश्यकता है। मैंने पायथन Django HttpResponse में कुछ हेडर सेट किए हैं। यदि आप अन्य प्रोग्रामिंग भाषाओं का उपयोग करते हैं तो आपको उनके अनुसार सेट करने की आवश्यकता है।

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

चूंकि मैं यहां xls (एक्सेल) डाउनलोड करता हूं, इसलिए मैंने contentType को एक से ऊपर समायोजित किया है। आपको इसे अपने फ़ाइल प्रकार के अनुसार सेट करने की आवश्यकता है। इस तकनीक का इस्तेमाल आप किसी भी तरह की फाइल को डाउनलोड करने के लिए कर सकते हैं।


33

आप किस सर्वर-साइड भाषा का उपयोग कर रहे हैं? अपने एप्लिकेशन में मैं आसानी से PHP के जवाब में सही हेडर सेट करके एक AJAX कॉल से एक फ़ाइल डाउनलोड कर सकता हूं:

हेडर सर्वर-साइड सेट करना

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;

यह वास्तव में इस डाउनलोड पृष्ठ पर ब्राउज़र को 'पुनर्निर्देशित' करेगा, लेकिन जैसा @ @ren ने अपनी टिप्पणी में कहा था, यह वर्तमान पृष्ठ से दूर नहीं जाएगा।

यह सब सही हेडर सेट करने के बारे में है, इसलिए मुझे यकीन है कि आप सर्वर-साइड भाषा के लिए एक उपयुक्त समाधान पाएंगे जो आप उपयोग कर रहे हैं यदि यह PHP नहीं है।

प्रतिक्रिया ग्राहक पक्ष को संभालना

मान लें कि आपको पहले से ही पता है कि AJAX कॉल कैसे किया जाता है, क्लाइंट की ओर से आप सर्वर के लिए AJAX के अनुरोध को निष्पादित करते हैं। सर्वर तब एक लिंक उत्पन्न करता है जहाँ से इस फ़ाइल को डाउनलोड किया जा सकता है, उदाहरण के लिए 'फ़ॉरवर्ड' URL जहाँ आप इंगित करना चाहते हैं। उदाहरण के लिए, सर्वर इसके साथ प्रतिक्रिया करता है:

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

प्रतिक्रिया को संसाधित करते समय, आप iframeअपने शरीर में एक इंजेक्षन करते हैं और अपने द्वारा iframeप्राप्त URL को उसी तरह एसआरसी सेट करते हैं (इस उदाहरण की आसानी के लिए jQuery का उपयोग करके):

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

यदि आपने सही हेडर सेट किया है जैसा कि ऊपर दिखाया गया है, तो iframe वर्तमान पृष्ठ से दूर ब्राउज़र को नेविगेट किए बिना एक डाउनलोड संवाद को बाध्य करेगा।

ध्यान दें

अपने प्रश्न के संबंध में अतिरिक्त जोड़; मुझे लगता है कि AJAX तकनीक के साथ सामान का अनुरोध करने पर JSON को हमेशा वापस करना सबसे अच्छा है। JSON प्रतिक्रिया प्राप्त करने के बाद, आप ग्राहक-पक्ष तय कर सकते हैं कि इसके साथ क्या करना है। हो सकता है, उदाहरण के लिए, बाद में आप चाहते हैं कि उपयोगकर्ता सीधे डाउनलोड को मजबूर करने के बजाय URL पर डाउनलोड लिंक पर क्लिक करें, अपने वर्तमान सेटअप में आपको ऐसा करने के लिए क्लाइंट और सर्वर-साइड दोनों को अपडेट करना होगा।


24

एक कोणीय दृष्टिकोण से समाधान की तलाश करने वालों के लिए, इसने मेरे लिए काम किया:

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

इससे मदद मिली, लेकिन मुझे मूल फ़ाइल नाम को संरक्षित करने की आवश्यकता है। मुझे "सामग्री-विवाद" के तहत प्रतिक्रिया शीर्षकों में फ़ाइलनाम दिखाई देता है, लेकिन मुझे कोड में प्रतिक्रिया ऑब्जेक्ट में वह मान नहीं मिल सकता है। link.download = ""एक यादृच्छिक गाइड फ़ाइल नाम में परिणाम सेट करना , और link.download = null"नल" नामक फ़ाइल में परिणाम होता है।
मेरी

@Marie, आप INPUTतत्व की HTMLInputElement.filesसंपत्ति का उपयोग करके अपलोड के समय फ़ाइल का नाम रिकॉर्ड कर सकते हैं। अधिक विस्तार के लिए फ़ाइल इनपुट पर MDN डॉक्स देखें ।
टिम हेटलर

बूँद का आकार सीमित है: stackoverflow.com/questions/28307789/…
user0800

22

यहां बताया गया है कि मुझे यह काम कैसे मिला 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

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


इसके लिए धन्यवाद, मैंने आज ही इसका उपयोग किया है। शानदार
रियान विल्सन

नमस्ते, क्या मुझे काम करने के लिए jQuery 3.0> की आवश्यकता है?
gbade_

मुझे आपके द्वारा दिए गए दोनों उदाहरणों के साथ एक रिक्त pdf भी मिल रही है। मैं इसे एक पीडीएफ फाइल डाउनलोड करने की कोशिश कर रहा हूं।
gbade_

@gbade_ नहीं, आपको किसी विशिष्ट jQuery संस्करण की आवश्यकता नहीं है। सभी संस्करणों के साथ ठीक काम करना चाहिए। क्या आपने जांच की है कि आपके द्वारा डाउनलोड की जाने वाली पीडीएफ में सही कोर हेडर हैं? कंसोल पर कोई भी त्रुटि डिबग करने में मदद कर सकती है
मयूर पद्शला

इसने मेरे लिए success: function (response, status, request) { download(response, "filename.txt", "application/text"); }
डाउनलोड.जेएस

12

मुझे लगता है कि आप पहले से ही एक समाधान पा चुके हैं, हालांकि मैं सिर्फ कुछ जानकारी जोड़ना चाहता था जो किसी को भी बड़े POST अनुरोधों के साथ एक ही चीज हासिल करने में मदद कर सकता है।

मेरे पास कुछ हफ़्ते पहले एक ही मुद्दा था, वास्तव में AJAX के माध्यम से "क्लीन" डाउनलोड को प्राप्त करना संभव नहीं है, फिलामेंट ग्रुप ने एक jQuery प्लगइन बनाया जो ठीक से काम करता है कि आपने पहले से कैसे पता लगाया है, इसे jQuery फ़ाइल कहा जाता है डाउनलोड हालांकि इस तकनीक के लिए एक नकारात्मक पहलू है।

यदि आप AJAX (फाइलों + 1 एमबी) के माध्यम से बड़े अनुरोध भेज रहे हैं तो यह जवाबदेही को नकारात्मक रूप से प्रभावित करेगा। धीमे इंटरनेट कनेक्शन में आपको अनुरोध भेजे जाने तक बहुत इंतजार करना होगा और फ़ाइल को डाउनलोड करने के लिए भी इंतजार करना होगा। यह एक त्वरित "क्लिक" => "पॉपअप" => "डाउनलोड प्रारंभ" की तरह नहीं है। यह "क्लिक" => "डेटा भेजे जाने तक प्रतीक्षा करें" => "प्रतिक्रिया के लिए प्रतीक्षा करें ="> "डाउनलोड प्रारंभ" है जो इसे फ़ाइल के आकार को दोगुना कर देता है क्योंकि आपको भेजे जाने के अनुरोध के लिए इंतजार करना होगा। AJAX के माध्यम से और इसे डाउनलोड करने योग्य फ़ाइल के रूप में वापस पाएं।

यदि आप छोटे फ़ाइल आकार <1MB के साथ काम कर रहे हैं तो आप इस पर ध्यान नहीं देंगे। लेकिन जैसा कि मैंने अपने ऐप में खोजा था, बड़े फ़ाइल आकारों के लिए यह लगभग असहनीय है।

मेरा ऐप उपयोगकर्ताओं को गतिशील रूप से उत्पन्न छवियों को निर्यात करने की अनुमति देता है, इन छवियों को सर्वर के लिए बेस 64 प्रारूप में POST अनुरोधों के माध्यम से भेजा जाता है (यह एकमात्र संभव तरीका है), फिर .png, .jpg फ़ाइलों, बेस 64 के रूप में उपयोगकर्ताओं को वापस भेजा और संसाधित किया जाता है। चित्र + 1MB के लिए स्ट्रिंग्स विशाल हैं, यह उपयोगकर्ताओं को फ़ाइल डाउनलोड करने के लिए आवश्यक से अधिक प्रतीक्षा करने के लिए मजबूर करता है। धीमी गति से इंटरनेट कनेक्शन में यह वास्तव में कष्टप्रद हो सकता है।

इसके लिए मेरा समाधान अस्थायी रूप से फ़ाइल को सर्वर पर लिखना था, एक बार तैयार होने के बाद, गतिशील रूप से बटन के रूप में फ़ाइल के लिए एक लिंक उत्पन्न होता है जो "कृपया प्रतीक्षा करें ..." और "डाउनलोड" राज्यों के बीच बदलता है और उसी पर समय, पूर्वावलोकन पॉपअप विंडो में बेस 64 छवि को प्रिंट करें ताकि उपयोगकर्ता "राइट-क्लिक" कर सकें और इसे सहेज सकें। यह उपयोगकर्ताओं के लिए सभी प्रतीक्षा समय को और अधिक अनुकूल बनाता है, और चीजों को गति भी देता है।

अद्यतन 30 सितंबर, 2014:

जब मैंने इसे पोस्ट किया, तब से महीनों बीत चुके हैं, आखिरकार मुझे बड़े बेस 64 स्ट्रिंग्स के साथ काम करते समय चीजों को गति देने के लिए एक बेहतर दृष्टिकोण मिला है। अब मैं बेस 64 स्ट्रिंग्स को डेटाबेस (लॉन्गटेक्स्ट या लॉन्गब्लॉग फील्ड्स का उपयोग करके) में स्टोर करता हूं, फिर मैं इसका रिकॉर्ड आईडी jQuery फाइल डाउनलोड के माध्यम से पास करता हूं, अंत में डाउनलोड स्क्रिप्ट फाइल पर मैं इस आईडी का उपयोग करके डेटाबेस को बेस 64 स्ट्रिंग को खींचने और पास करने के लिए क्वेरी करता हूं। डाउनलोड समारोह।

डाउनलोड स्क्रिप्ट उदाहरण:

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

मुझे पता है कि ओपी ने जो पूछा उससे परे यह तरीका है, हालांकि मुझे लगा कि मेरे जवाब को अपने निष्कर्षों के साथ अपडेट करना अच्छा होगा। जब मैं अपनी समस्या के समाधान के लिए खोज कर रहा था, मैंने बहुत सारे "AJAX POST डेटा से डाउनलोड करें" थ्रेड्स पढ़े, जो मुझे वह उत्तर नहीं दे रहा था जिसकी मुझे तलाश थी, मुझे उम्मीद है कि यह जानकारी किसी को इस तरह से कुछ हासिल करने में मदद करती है।


jQuery File Downloadकेवल मुझे URL पर रीडायरेक्ट। मैं इसे इस तरह कहता हूं jQuery.download("api/ide/download-this-file.php", {filePath: path2Down}, "POST");:।
कैस्पर

5

मैं कुछ कठिनाइयों को इंगित करना चाहता हूं जो स्वीकृत उत्तर में तकनीक का उपयोग करते समय उत्पन्न होती हैं, अर्थात एक फॉर्म पोस्ट का उपयोग करके:

  1. आप अनुरोध पर हेडर सेट नहीं कर सकते। यदि आपके प्रमाणीकरण स्कीमा में हेडर शामिल हैं, तो ऑथोराइजेशन हेडर में पास किया गया एक Json-Web-Token, आपको इसे भेजने का अन्य तरीका खोजना होगा, उदाहरण के लिए क्वेरी पैरामीटर।

  2. जब अनुरोध समाप्त हो गया हो तो आप वास्तव में नहीं बता सकते। ठीक है, आप एक कुकी का उपयोग कर सकते हैं जो प्रतिक्रिया पर सेट हो जाती है, जैसा कि jquery.fileDownload द्वारा किया जाता है , लेकिन यह परिपूर्ण से FAR है। यह समवर्ती अनुरोधों के लिए काम नहीं करेगा और अगर प्रतिक्रिया कभी नहीं आती है तो यह टूट जाएगा।

  3. यदि सर्वर एक त्रुटि के साथ प्रतिक्रिया करता है, तो उपयोगकर्ता को त्रुटि पृष्ठ पर पुनः निर्देशित किया जाएगा।

  4. आप केवल फ़ॉर्म द्वारा समर्थित सामग्री प्रकारों का उपयोग कर सकते हैं । जिसका अर्थ है कि आप JSON का उपयोग नहीं कर सकते।

मैंने फ़ाइल को S3 पर सहेजने और फ़ाइल प्राप्त करने के लिए पूर्व-हस्ताक्षरित URL भेजने की विधि का उपयोग करके समाप्त कर दिया।


5

अधिक आधुनिक दृष्टिकोण की तलाश करने वालों के लिए, आप इसका उपयोग कर सकते हैं fetch API। निम्न उदाहरण दिखाता है कि स्प्रेडशीट फ़ाइल को कैसे डाउनलोड किया जाए। यह आसानी से निम्नलिखित कोड के साथ किया जाता है।

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

मेरा मानना ​​है कि यह दृष्टिकोण अन्य XMLHttpRequestसमाधानों की तुलना में समझने में बहुत आसान है । इसके अलावा, यह jQueryदृष्टिकोण के लिए एक समान वाक्यविन्यास है , बिना किसी अतिरिक्त पुस्तकालयों को जोड़ने की आवश्यकता के बिना।

निश्चित रूप से, मैं आपको सलाह दूंगा कि आप किस ब्राउज़र को विकसित कर रहे हैं, क्योंकि यह नया दृष्टिकोण IE पर काम नहीं करेगा। आप निम्नलिखित [लिंक] [1] पर पूर्ण ब्राउज़र संगतता सूची पा सकते हैं।

महत्वपूर्ण : इस उदाहरण में मैं दिए गए सर्वर पर एक JSON अनुरोध भेज रहा हूं url। यह urlसेट किया जाना चाहिए, मेरे उदाहरण पर मैं आपको इस भाग को जानता हूं। इसके अलावा, काम करने के आपके अनुरोध के लिए आवश्यक हेडर पर विचार करें। चूँकि मैं JSON भेज रहा हूँ, मुझे Content-Typeहेडर जोड़ना होगा और इसे सेट करना होगा application/json; charset=utf-8, जैसा कि सर्वर को यह पता चलेगा कि उसे किस प्रकार का अनुरोध प्राप्त होगा।


1
बहुत बढ़िया! डाउनलोड पॉपअप के बजाय इसे एक नए टैब में खोलने के लिए: `` कास्ट विंडो = ओपन (डाउनलोड, "_blank") का उपयोग करें; if (window! == null) window.focus (); `` `
andy

क्या मेरे लिए डेटा के कई सेटों के लिए ऐसा करने का कोई तरीका है? उदाहरण के लिए, {fileOne: data, fileTwo: data, fileThree: data} वापस प्राप्त करें और आपी कॉल से एक बार में तीन डाउनलोड की गई फ़ाइलों को जनरेट करें? धन्यवाद!
il0v3d0g 17

हम्म, मैंने कोशिश नहीं की है। लेकिन आप हमेशा छवियों को एक ज़िप फ़ाइल में संपीड़ित कर सकते हैं और इसे डाउनलोड कर सकते हैं। मैं जांच करूंगा कि क्या यह संभव है।
एलैन क्रूज़

4

यहाँ एक अस्थायी छिपे हुए रूप का उपयोग करके मेरा समाधान है।

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

ध्यान दें कि मैं बड़े पैमाने पर JQuery का उपयोग करता हूं, लेकिन आप देशी JS के साथ भी ऐसा कर सकते हैं।


3

जैसा कि अन्य लोगों ने कहा है, आप पोस्ट अनुरोध के माध्यम से डाउनलोड करने के लिए एक फॉर्म बना सकते हैं और जमा कर सकते हैं। हालाँकि, आपको इसे मैन्युअल रूप से करने की आवश्यकता नहीं है।

वास्तव में ऐसा करने के लिए एक साधारण पुस्तकालय jquery.redirect है । यह मानक jQuery.postविधि के समान एक एपीआई प्रदान करता है :

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

3

यह 3 साल पुराना सवाल है लेकिन मुझे आज भी यही समस्या थी। मैंने आपका संपादित समाधान देखा, लेकिन मुझे लगता है कि यह प्रदर्शन का त्याग कर सकता है क्योंकि इसे दोहरा अनुरोध करना होगा। इसलिए अगर किसी को एक और समाधान की आवश्यकता है जो सेवा को दो बार कॉल करने की आवश्यकता नहीं है तो यह तरीका है जो मैंने किया है:

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

यह फ़ॉर्म केवल सेवा को कॉल करने और window.location () का उपयोग करने से बचने के लिए उपयोग किया जाता है। उसके बाद आपको बस सेवा को कॉल करने और फ़ाइल प्राप्त करने के लिए jquery से एक फॉर्म सबमिट करना होगा। यह बहुत आसान है, लेकिन इस तरह आप एक पोस्ट का उपयोग करके एक डाउनलोड कर सकते हैं । अब मुझे लगता है कि यह आसान हो सकता है यदि आप जिस सेवा को कॉल कर रहे हैं वह एक GET है , लेकिन यह मेरा मामला नहीं है।


1
यह एक अजाक्स पोस्ट नहीं है क्योंकि सवाल अजाक्स का उपयोग कर रहा है
निधिन डेविड

यह सिर्फ ऊपर की समस्या का समाधान है, हालांकि अजाक्स कॉल के लिए नहीं।
नोमी अली

1

मैं इस FileSaver.js का इस्तेमाल किया । सीएसवी फ़ाइलों के साथ मेरे मामले में, मैंने ऐसा किया (कॉफ़ीस्क्रिप्ट में):

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

मुझे लगता है कि सबसे जटिल मामले के लिए, डेटा को ठीक से संसाधित किया जाना चाहिए। हूड FileSaver.js के तहत जोनाथन अमेंड के जवाब के एक ही दृष्टिकोण को लागू करते हैं ।


1
.. लेकिन क्या आप आमतौर पर आईओएस में फाइलें डाउनलोड कर सकते हैं?
एलेक्स मार्शल

1

देखें: http://www.henryalgus.com/reading-binary-files-use-jquery-ajax/ यह एक प्रतिक्रिया के रूप में एक बूँद लौटाएगा, जिसे बाद में फाइलसेवर में डाला जा सकता है


2
जब भी यह सैद्धांतिक रूप से प्रश्न का उत्तर दे सकता है, तो उत्तर के आवश्यक भागों को शामिल करना और संदर्भ के लिए लिंक प्रदान करना बेहतर होगा
भार्गव राव

1

जोनाथन एमेंड्स को एज में काम करने का जवाब पाने के लिए मैंने निम्नलिखित बदलाव किए:

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

इसके लिए

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

मैं इसे एक टिप्पणी के रूप में पोस्ट करूंगा, लेकिन मेरे पास इसके लिए पर्याप्त प्रतिष्ठा नहीं है


0

यहाँ मेरा समाधान है, विभिन्न स्रोतों से एकत्र: सर्वर साइड कार्यान्वयन:

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

ग्राहक पक्ष कार्यान्वयन (jquery का उपयोग करके):

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

अजाक्स में वेब पेज डाउनलोड करने का एक और उपाय है। लेकिन मैं एक ऐसे पृष्ठ की बात कर रहा हूँ जिसे पहले संसाधित किया जाना चाहिए और फिर डाउनलोड किया जाना चाहिए।

सबसे पहले आपको परिणाम डाउनलोड करने से पेज प्रसंस्करण को अलग करना होगा।

1) अजाक्स कॉल में केवल पृष्ठ गणना की जाती है।

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

       फ़ंक्शन (डेटा, स्थिति) 
       {
            अगर (स्थिति == "सफलता") 
            {
                / * 2) उत्तर में वह पृष्ठ जो पिछली गणनाओं का उपयोग करता है, डाउनलोड किया जाता है। उदाहरण के लिए, यह एक पृष्ठ हो सकता है जो अजाक्स कॉल में गणना की गई तालिका के परिणामों को प्रिंट करता है। * /
                window.location.href = DownloadPage.php + "; ID =" + 29;
            }               
       }
);

// उदाहरण के लिए: कैल्क्युपासपेज.फैप में

    अगर (खाली ($ _ POST ["कलन विधि"])) 
    {
        $ आईडी = $ _POST ["आईडी"];

        $ क्वेरी = "INSERT INTO ExamplePage (data1, data2) VALUES ('"। $ _ POST ["data1"]। "', '"। $ _ POST ["data2"] "" "WHERE id =" $ ID
        ...
    }

// उदाहरण के लिए: DownloadPage.php में

    $ आईडी = $ _GET ["आईडी"];

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

    $ फ़ाइल नाम = "Export_Data.xls";
    शीर्ष लेख ("सामग्री-प्रकार: अनुप्रयोग / vnd.ms-excel");
    शीर्ष लेख ("सामग्री-विवाद: इनलाइन; फ़ाइल नाम = $ फ़ाइल नाम");

    ...

मुझे उम्मीद है कि यह समाधान कई लोगों के लिए उपयोगी हो सकता है, क्योंकि यह मेरे लिए था।


0

यदि प्रतिक्रिया एक ऐरे बफर है , तो इसे अजाक्स में असफल घटना के तहत आज़माएं:

 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;
      }
  • जहां x.d इवेंट की सफलता समारोह में event.data को प्रतिक्रिया मिली है।

0

नीचे कुछ सूची के आधार पर कई फ़ाइलों को डाउनलोड करने के लिए मेरा समाधान है जिसमें कुछ आईडी हैं और डेटाबेस में देख रहे हैं, फाइलें निर्धारित की जाएंगी और डाउनलोड के लिए तैयार होंगी - यदि वे मौजूद हैं। मैं अजाक्स का उपयोग करके प्रत्येक फ़ाइल के लिए C # MVC कार्रवाई कह रहा हूं।

और हां, जैसा कि दूसरों ने कहा, यह jQuery के अजाक्स में करना संभव है। मैंने इसे अजाक्स की सफलता के साथ किया और मैं हमेशा प्रतिक्रिया 200 भेज रहा हूं।

तो, यह कुंजी है:

  success: function (data, textStatus, xhr) {

और यह मेरा कोड है:

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

फिर कॉलिंग:

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 वापस करते हैं, तब तक अजाक्स में सफलता इसके साथ काम कर सकती है, आप जांच सकते हैं कि क्या वास्तव में फाइल मौजूद है या नहीं, क्योंकि इस मामले में नीचे की रेखा झूठी होगी और आप उपयोगकर्ता को इसके बारे में सूचित कर सकते हैं:

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

0

मुझे @ alain-cruz's के समान समाधान की आवश्यकता थी, लेकिन कई डाउनलोड के साथ nuxt / vue में। मुझे पता है कि ब्राउज़र कई फ़ाइल डाउनलोड को ब्लॉक करते हैं, और मेरे पास एपीआई भी है जो सीएसवी स्वरूपित डेटा का एक सेट लौटाता है। मैं पहली बार JSZip का उपयोग करने जा रहा था, लेकिन मुझे IE समर्थन की आवश्यकता थी इसलिए यहां मेरा समाधान है। अगर कोई मुझे यह सुधारने में मदद कर सकता है तो यह बहुत अच्छा होगा, लेकिन यह अब तक मेरे लिए काम कर रहा है।

एपीआई रिटर्न:

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();
   },
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.