javascript - JSON डेटा के साथ POST के माध्यम से फ़ाइल डाउनलोड करने के लिए जावास्क्रिप्ट/jQuery




ajax rest (8)

एक आसान तरीका है, एक फॉर्म बनाएं और इसे पोस्ट करें, यह पृष्ठ को रीसेट करने का जोखिम चलाता है यदि रिटर्न माइम प्रकार कुछ ऐसा होता है जो ब्राउज़र खुलता है, लेकिन सीएसवी के लिए और यह सही है

उदाहरण के लिए अंडरस्कोर और jquery की आवश्यकता है

var postData = {
    filename:filename,
    filecontent:filecontent
};
var fakeFormHtmlFragment = "<form style='display: none;' method='POST' action='"+SAVEAS_PHP_MODE_URL+"'>";
_.each(postData, function(postValue, postKey){
    var escapedKey = postKey.replace("\\", "\\\\").replace("'", "\'");
    var escapedValue = postValue.replace("\\", "\\\\").replace("'", "\'");
    fakeFormHtmlFragment += "<input type='hidden' name='"+escapedKey+"' value='"+escapedValue+"'>";
});
fakeFormHtmlFragment += "</form>";
$fakeFormDom = $(fakeFormHtmlFragment);
$("body").append($fakeFormDom);
$fakeFormDom.submit();

एचटीएमएल, टेक्स्ट और इस तरह की चीजों के लिए, सुनिश्चित करें कि माइमटाइप एप्लिकेशन / ऑक्टेट-स्ट्रीम जैसी कुछ चीज है

PHP कोड

<?php
/**
 * get HTTP POST variable which is a string ?foo=bar
 * @param string $param
 * @param bool $required
 * @return string
 */
function getHTTPPostString ($param, $required = false) {
    if(!isset($_POST[$param])) {
        if($required) {
            echo "required POST param '$param' missing";
            exit 1;
        } else {
            return "";
        }
    }
    return trim($_POST[$param]);
}

$filename = getHTTPPostString("filename", true);
$filecontent = getHTTPPostString("filecontent", true);

header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$filename\"");
echo $filecontent;

मेरे पास एक jquery- आधारित सिंगल-पेज वेबपैप है। यह AJAX कॉल के माध्यम से एक विश्वसनीय वेब सेवा के साथ संचार करता है।

मैं निम्नलिखित को पूरा करने की कोशिश कर रहा हूं:

  1. एक पोस्ट सबमिट करें जिसमें एक आरईएसटी यूआरएल में JSON डेटा शामिल है।
  2. अगर अनुरोध JSON प्रतिक्रिया निर्दिष्ट करता है, तो JSON वापस आ गया है।
  3. अगर अनुरोध एक पीडीएफ / एक्सएलएस / आदि प्रतिक्रिया निर्दिष्ट करता है, तो डाउनलोड करने योग्य बाइनरी लौटा दी जाती है।

मेरे पास अब 1 और 2 काम कर रहा है, और क्लाइंट jquery ऐप जेएसओएन डेटा के आधार पर डीओएम तत्व बनाकर वेब पेज में लौटा हुआ डेटा प्रदर्शित करता है। मेरे पास वेब-सेवा बिंदु दृश्य से # 3 भी काम कर रहा है, जिसका अर्थ है कि सही JSON पैरामीटर दिए जाने पर यह बाइनरी फ़ाइल बना और वापस कर देगा। लेकिन मैं क्लाइंट जावास्क्रिप्ट कोड में # 3 से निपटने का सबसे अच्छा तरीका अनिश्चित हूं।

क्या इस तरह एजेक्स कॉल से एक डाउनलोड करने योग्य फ़ाइल वापस प्राप्त करना संभव है? मैं ब्राउज़र को फ़ाइल डाउनलोड और सहेजने के लिए कैसे प्राप्त करूं?

$.ajax({
    type: "POST",
    url: "/services/test",
    contentType: "application/json",
    data: JSON.stringify({category: 42, sort: 3, type: "pdf"}),
    dataType: "json",
    success: function(json, status){
        if (status != "success") {
            log("Error loading data");
            return;
        }
        log("Data loaded!");
    },
    error: function(result, status, err) {
        log("Error loading data");
        return;
    }
});

सर्वर निम्न शीर्षकों के साथ प्रतिक्रिया करता है:

Content-Disposition:attachment; filename=export-1282022272283.pdf
Content-Length:5120
Content-Type:application/pdf
Server:Jetty(6.1.11)

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

success: function(json,status) {
    window.location.href = json.url;
}

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

इसे पूरा करने के लिए एक आसान तरीका होना चाहिए। विचार?

संपादित करें: $ .ajax के लिए दस्तावेज़ों की समीक्षा करने के बाद, मुझे लगता है कि प्रतिक्रिया xml, html, script, json, jsonp, text केवल xml, html, script, json, jsonp, text , इसलिए मुझे लगता है कि फ़ाइल का उपयोग करके सीधे फ़ाइल डाउनलोड करने का कोई तरीका नहीं है AJAX अनुरोध, जब तक मैं @VinayC उत्तर में सुझाए गए अनुसार डेटा यूआरआई योजना का उपयोग करने में बाइनरी फ़ाइल एम्बेड नहीं करता (जो ऐसा कुछ नहीं है जिसे मैं करना चाहता हूं)।

तो मुझे लगता है कि मेरे विकल्प हैं:

  1. AJAX का उपयोग न करें और इसके बजाय एक फॉर्म पोस्ट सबमिट करें और मेरे JSON डेटा को फॉर्म मानों में एम्बेड करें। शायद छिपा iframes और इस तरह के साथ गड़बड़ की जरूरत होगी।

  2. AJAX का उपयोग न करें और इसके बजाय मेरे JSON डेटा को एक मानक स्ट्रेट बनाने के लिए क्वेरी स्ट्रिंग में कनवर्ट करें और इस URL पर window.location.href सेट करें। ब्राउज़र को एप्लिकेशन यूआरएल से बदलने से रोकने के लिए मेरे क्लिक हैंडलर में event.preventDefault () का उपयोग करने की आवश्यकता हो सकती है।

  3. उपरोक्त मेरे अन्य विचार का प्रयोग करें, लेकिन @nikus उत्तर से सुझावों के साथ बढ़ाया गया है। कुछ पैरामीटर के साथ AJAX अनुरोध सबमिट करें जो वेब-सेवा को यह बताए कि इसे AJAX कॉल के माध्यम से बुलाया जा रहा है। यदि वेब सेवा को अजाक्स कॉल से बुलाया जाता है, तो जेनरेट संसाधन पर यूआरएल के साथ जेएसओएन वापस कर दें। अगर संसाधन सीधे कहा जाता है, तो वास्तविक बाइनरी फ़ाइल वापस करें।

जितना अधिक मैं इसके बारे में सोचता हूं, उतना ही मुझे अंतिम विकल्प पसंद है। इस तरह से मैं अनुरोध के बारे में जानकारी प्राप्त कर सकता हूं (उत्पन्न करने का समय, फ़ाइल का आकार, त्रुटि संदेश इत्यादि) और मैं डाउनलोड शुरू करने से पहले उस जानकारी पर कार्य कर सकता हूं। डाउनसाइड सर्वर पर अतिरिक्त फ़ाइल प्रबंधन है।

इसे पूरा करने के किसी अन्य तरीके? इन तरीकों के लिए कोई भी पेशेवर / विपक्ष मुझे अवगत होना चाहिए?


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

const a = document.createElement('a');
a.download = '';
a.href = urlForPdfFile;
a.click();

सब कुछ कर दिया।

यदि आप डाउनलोड के लिए एक विशेष नाम चाहते हैं, तो इसे download एट्रिब्यूट में पास करें:

const a = document.createElement('a');
a.download = 'my-special-name.pdf';
a.href = urlForPdfFile;
a.click();

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

मैंने जो किया वह दो सर्वर फ़ंक्शन बना रहा था। पहले डेटा सत्यापित किया गया। अगर कोई त्रुटि हुई, तो उसे वापस कर दिया जाएगा। अगर यह कोई त्रुटि नहीं थी, तो मैंने बेस 64 स्ट्रिंग के रूप में क्रमबद्ध / एन्कोड किए गए सभी पैरामीटर लौटा दिए। फिर, क्लाइंट पर, मेरे पास एक ऐसा फॉर्म है जिसमें दूसरे सर्वर फ़ंक्शन में केवल एक छिपी हुई इनपुट और पोस्ट हों। मैंने बेस 64 स्ट्रिंग में छुपा इनपुट सेट किया है और प्रारूप सबमिट किया है। दूसरा सर्वर फ़ंक्शन पैरामीटर को डीकोड / deserializes और फ़ाइल उत्पन्न करता है। फॉर्म पृष्ठ पर एक नई विंडो या आईफ्रेम पर सबमिट कर सकता है और फ़ाइल खुल जाएगी।

इसमें थोड़ा और काम शामिल है, और शायद थोड़ा और प्रसंस्करण, लेकिन कुल मिलाकर, मुझे इस समाधान के साथ बहुत अच्छा लगा।

कोड सी # / एमवीसी में है

    public JsonResult Validate(int reportId, string format, ReportParamModel[] parameters)
    {
        // TODO: do validation

        if (valid)
        {
            GenerateParams generateParams = new GenerateParams(reportId, format, parameters);

            string data = new EntityBase64Converter<GenerateParams>().ToBase64(generateParams);

            return Json(new { State = "Success", Data = data });
        }

        return Json(new { State = "Error", Data = "Error message" });
    }

    public ActionResult Generate(string data)
    {
        GenerateParams generateParams = new EntityBase64Converter<GenerateParams>().ToEntity(data);

        // TODO: Generate file

        return File(bytes, mimeType);
    }

ग्राहक पर

    function generate(reportId, format, parameters)
    {
        var data = {
            reportId: reportId,
            format: format,
            params: params
        };

        $.ajax(
        {
            url: "/Validate",
            type: 'POST',
            data: JSON.stringify(data),
            dataType: 'json',
            contentType: 'application/json; charset=utf-8',
            success: generateComplete
        });
    }

    function generateComplete(result)
    {
        if (result.State == "Success")
        {
            // this could/should already be set in the HTML
            formGenerate.action = "/Generate";
            formGenerate.target = iframeFile;

            hidData = result.Data;
            formGenerate.submit();
        }
        else
            // TODO: display error messages
    }

मुझे लगता है कि संयोजन का उपयोग करने का सबसे अच्छा तरीका है, आपका दूसरा दृष्टिकोण एक सुरुचिपूर्ण समाधान प्रतीत होता है जहां ब्राउज़र शामिल हैं।

तो कॉल के तरीके के आधार पर। (चाहे वह एक ब्राउज़र या वेब सेवा कॉल हो) आप ब्राउज़र के लिए एक यूआरएल भेजने और किसी अन्य वेब सेवा क्लाइंट को कच्चे डेटा भेजने के साथ दोनों के संयोजन का उपयोग कर सकते हैं।


मैं ब्लॉब्स का उपयोग करने वाले दूसरे विकल्प के साथ खेल रहा हूं। मैंने इसे टेक्स्ट दस्तावेज़ डाउनलोड करने में कामयाब रहा है, और मैंने पीडीएफ डाउनलोड किया है (हालांकि वे दूषित हैं)।

ब्लॉब एपीआई का उपयोग करके आप निम्न कार्य करने में सक्षम होंगे:

$.post(/*...*/,function (result)
{
    var blob=new Blob([result]);
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="myFileName.txt";
    link.click();

});

यह आईई 10+, क्रोम 8+, एफएफ 4+ है। https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL देखें

यह केवल क्रोम, फ़ायरफ़ॉक्स और ओपेरा में फ़ाइल डाउनलोड करेगा। ब्राउजर को इसे डाउनलोड करने के लिए मजबूर करने के लिए यह एंकर टैग पर एक डाउनलोड विशेषता का उपयोग करता है।


यह कुछ समय हो गया है क्योंकि इस सवाल से पूछा गया था लेकिन मुझे एक ही चुनौती थी और मेरा समाधान साझा करना चाहता था। यह अन्य उत्तरों के तत्वों का उपयोग करता है लेकिन मैं इसे पूरी तरह से नहीं ढूंढ पाया। यह किसी फॉर्म या आइफ्रेम का उपयोग नहीं करता है लेकिन इसे एक पोस्ट / अनुरोध अनुरोध जोड़ी की आवश्यकता होती है। अनुरोधों के बीच फ़ाइल को सहेजने के बजाय, यह पोस्ट डेटा बचाता है। ऐसा लगता है कि यह सरल और प्रभावी दोनों है।

ग्राहक

var apples = new Array(); 
// construct data - replace with your own
$.ajax({
   type: "POST",
   url: '/Home/Download',
   data: JSON.stringify(apples),
   contentType: "application/json",
   dataType: "text",

   success: function (data) {
      var url = '/Home/Download?id=' + data;
      window.location = url;
   });
});

सर्वर

[HttpPost]
// called first
public ActionResult Download(Apple[] apples)
{
   string json = new JavaScriptSerializer().Serialize(apples);
   string id = Guid.NewGuid().ToString();
   string path = Server.MapPath(string.Format("~/temp/{0}.json", id));
   System.IO.File.WriteAllText(path, json);

   return Content(id);
}

// called next
public ActionResult Download(string id)
{
   string path = Server.MapPath(string.Format("~/temp/{0}.json", id));
   string json = System.IO.File.ReadAllText(path);
   System.IO.File.Delete(path);
   Apple[] apples = new JavaScriptSerializer().Deserialize<Apple[]>(json);

   // work with apples to build your file in memory
   byte[] file = createPdf(apples); 

   Response.AddHeader("Content-Disposition", "attachment; filename=juicy.pdf");
   return File(file, "application/pdf");
}

सर्वर पर फ़ाइल को सहेजने और इसे पुनर्प्राप्त करने के बजाय, एक अन्य दृष्टिकोण, दूसरी क्रिया तक एक छोटी समाप्ति के साथ .NET 4.0+ ऑब्जेक्ट कैश का उपयोग करना है (जिस समय इसे निश्चित रूप से डंप किया जा सकता है)। कॉल करने के लिए मैं JQuery अजाक्स का उपयोग करना चाहता हूं, यह है कि यह असीमित है। मेरी गतिशील पीडीएफ फ़ाइल को बनाने में काफी समय लगता है, और मैं उस समय के दौरान एक व्यस्त स्पिनर संवाद प्रदर्शित करता हूं (यह अन्य काम करने की भी अनुमति देता है)। ब्लॉब बनाने के लिए "सफलता:" में लौटाए गए डेटा का उपयोग करने का दृष्टिकोण विश्वसनीय रूप से काम नहीं करता है। यह पीडीएफ फाइल की सामग्री पर निर्भर करता है। यह प्रतिक्रिया में डेटा द्वारा आसानी से दूषित हो जाता है, अगर यह पूरी तरह से पाठ्यचर्या नहीं है जो कि अजाक्स संभाल सकता है।


Letronje का समाधान केवल बहुत ही सरल पृष्ठों के लिए काम करता है। document.body.innerHTML += शरीर का HTML टेक्स्ट लेता है, आईफ़्रेम HTML जोड़ता है, और उस स्ट्रिंग पर पृष्ठ के आंतरिक HTML को सेट करता है। यह अन्य चीजों के साथ आपके पृष्ठ के किसी भी ईवेंट बाइंडिंग को मिटा देगा। एक तत्व बनाएँ और इसके बजाय appendChild उपयोग करें।

$.post('/create_binary_file.php', postData, function(retData) {
  var iframe = document.createElement("iframe");
  iframe.setAttribute("src", retData.url);
  iframe.setAttribute("style", "display: none");
  document.body.appendChild(iframe);
}); 

या jQuery का उपयोग कर

$.post('/create_binary_file.php', postData, function(retData) {
  $("body").append("<iframe src='" + retData.url+ "' style='display: none;' ></iframe>");
}); 

यह वास्तव में क्या करता है: परिवर्तनीय पोस्टडेटा में डेटा के साथ /create_binary_file.php पर एक पोस्ट करें; यदि वह पोस्ट सफलतापूर्वक पूर्ण हो जाती है, तो पृष्ठ के मुख्य भाग में एक नया आईफ्रेम जोड़ें। धारणा यह है कि /create_binary_file.php की प्रतिक्रिया में एक मान 'यूआरएल' शामिल होगा, जो यूआरएल है जो उत्पन्न पीडीएफ / एक्सएलएस / आदि फ़ाइल से डाउनलोड किया जा सकता है। उस पृष्ठ पर एक आईफ्रेम जोड़ना जो उस URL का संदर्भ देगा, जिसके परिणामस्वरूप ब्राउजर उपयोगकर्ता को फ़ाइल डाउनलोड करने के लिए प्रोत्साहित करता है, यह मानते हुए कि वेब सर्वर के पास उपयुक्त माइम प्रकार कॉन्फ़िगरेशन है।





rest