डाउनलोड करें और Ajax का उपयोग करके पीडीएफ फाइल खोलें


98

मेरे पास एक एक्शन क्लास है जो एक पीडीएफ उत्पन्न करता है। contentTypeउचित रूप में निर्धारित किया गया है।

public class MyAction extends ActionSupport 
{
   public String execute() {
    ...
    ...
    File report = signedPdfExporter.generateReport(xyzData, props);

    inputStream = new FileInputStream(report);
    contentDisposition = "attachment=\"" + report.getName() + "\"";
    contentType = "application/pdf";
    return SUCCESS;
   }
}

मैं इसे action अजाक्स कॉल के माध्यम से कहता हूं । मुझे इस स्ट्रीम को ब्राउज़र में पहुंचाने का तरीका नहीं पता है। मैंने कुछ चीजों की कोशिश की लेकिन कुछ भी काम नहीं आया।

$.ajax({
    type: "POST",
    url: url,
    data: wireIdList,
    cache: false,
    success: function(response)
    {
        alert('got response');
        window.open(response);
    },
    error: function (XMLHttpRequest, textStatus, errorThrown) 
    {
        alert('Error occurred while opening fax template' 
              + getAjaxErrorString(textStatus, errorThrown));
    }
});

उपरोक्त त्रुटि देता है:

आपके ब्राउज़र ने एक अनुरोध भेजा है जिसे यह सर्वर समझ नहीं सका।

जवाबों:


37

इसके लिए आपको अजाक्स की आवश्यकता नहीं है। बस एक <a>लिंक यदि आप सेट के लिए पर्याप्त है content-dispositionकरने के लिए attachmentसर्वर साइड कोड में। इस तरह मूल पृष्ठ केवल खुला रहेगा, यदि वह आपकी प्रमुख चिंता थी (तो आप अनावश्यक रूप से इसके लिए अजाक्स को क्यों चुनेंगे?)। इसके अलावा, इस अच्छी तरह से acynchronously संभाल करने के लिए कोई रास्ता नहीं है। पीडीएफ चरित्र डेटा नहीं है। यह बाइनरी डेटा है। आप सामान पसंद नहीं कर सकते $(element).load()। आप इसके लिए पूरी तरह से नए अनुरोध का उपयोग करना चाहते हैं । उसके लिए <a href="pdfservlet/filename.pdf">pdf</a>पूरी तरह उपयुक्त है।

सर्वर साइड कोड के साथ आपको अधिक सहायता करने के लिए, आपको उपयोग की गई भाषा के बारे में और कोड प्रयासों का एक अंश पोस्ट करने की आवश्यकता होगी।


7
एक बार फिर: आपको इसके लिए अजाक्स की आवश्यकता नहीं है। यह केवल परेशानी के लिए पूछ रहा है। पीडीएफ बाइनरी डेटा है, HTML या JSON जैसे वर्ण डेटा नहीं।
BalusC

3
var url = referencePath + "/xyz/blahBlah.action"; url + = url + "?" + परम; try {var child = window.open (url); child.focus (); } catch (e) {}
Nayn

5
कुछ ब्राउज़रों में window.open खुला और खाली रहेगा, जो एंड्यूज़र्स के लिए कष्टप्रद हो सकता है। तो, इसके लिए भी window.open का उपयोग न करें। यदि इसके content-dispositionलिए सेट किया गया है attachment, तो आपको सिर्फ एक Save asसंवाद मिलेगा । मूल पृष्ठ अपरिवर्तित रहेगा। बस <a href="pdfservlet/filename.pdf">pdf</a>या <form action="pdfservlet/filename.pdf"><input type="submit"></form>पर्याप्त से अधिक है।
बालुसक

5
एक सीमित उरल लंबाई है। और लेखक POST के बारे में पूछ रहा है।
एडवर्ड ओलिमिसन

3
@EdwardOlamisan से सहमत, यह सही उत्तर नहीं है क्योंकि लेखक POSTडेटा की कोशिश कर रहा था ।
adjj

122

यहां बताया गया है कि मुझे यह काम कैसे मिला

$.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();
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

अपडेट किया गया जवाब का उपयोग कर download.js

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


29
क्या यह क्रोम पर काम करता है? मैं केवल एक रिक्त पीडीएफ देख सकता हूं।
तरुण गुप्ता

1
हां, यह सभी आधुनिक ब्राउज़रों पर काम करता है। यदि आप एक रिक्त pdf देखते हैं, तो ajax url को एक नए टैब में चलाने का प्रयास करें। अगर आपको वहां भी एक खाली स्क्रीन मिलती है, तो आपको स्वयं पीडीएफ की समस्या हो सकती है। अगर आपको वहां पीडीएफ फाइल दिखाई देती है और डाउनलोड की गई फाइल में नहीं, तो मुझे अपने ईमेल पर बताएं। :)
मयूर पद्शला

5
यह (एंकर तत्व) वास्तव में IE 11, एज और फ़ायरफ़ॉक्स पर मेरे लिए काम नहीं करता था। सफलता को केवल "window.open (URL.createObjectURL (बूँद))" के उपयोग से परिवर्तित किया।
जिमीस्वाइन

3
पीडीएफ फाइल डाउनलोड हो गई है लेकिन कोई भी सामग्री उपलब्ध नहीं है। मेरे पास सर्वर साइड में बाइट [] है और पीडीएफ कंटेंट उपलब्ध है। plz का सुझाव
अवनीश कुमार

4
रिक्त पीडीएफ फाइल डाउनलोड की जाती है।
फारुख

31

मैं वास्तव में नहीं सोचता कि अतीत के किसी भी उत्तर ने मूल पोस्टर की समस्या को हल किया। वे सभी एक GET अनुरोध मानते हैं, जबकि पोस्टर POST डेटा की कोशिश कर रहा था और प्रतिक्रिया में एक डाउनलोड प्राप्त कर रहा था।

किसी भी बेहतर उत्तर की खोज के दौरान हमने इस jQuery प्लगइन को अजाक्स की तरह फाइल डाउनलोड करने के लिए अनुरोध किया

अपने "दिल" में यह एक "अस्थायी" HTML फॉर्म बनाता है जिसमें इनपुट फ़ील्ड के रूप में दिए गए डेटा होते हैं। यह फ़ॉर्म दस्तावेज़ में संलग्न है और वांछित URL पर पोस्ट किया गया है। उसके बाद फार्म फिर से हटा दिया जाता है:

jQuery('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
    .appendTo('body').submit().remove()

अपडेट किया गया मयूर का जवाब jQuery प्लग-इन I की तुलना में बहुत ही आशाजनक और बहुत सरल लग रहा है।


9

इस तरह से मैं इस मुद्दे को हल करता हूं। इस पोस्ट
पर जोनाथन अमेंड के जवाब से मुझे बहुत मदद मिली। नीचे दिए गए उदाहरण को सरल किया गया है।

अधिक जानकारी के लिए, उपरोक्त स्रोत कोड एक JQuery Ajax अनुरोध (GET, POST, PUT आदि) का उपयोग करके एक फ़ाइल डाउनलोड करने में सक्षम है । यह, JSON के रूप में मापदंडों को अपलोड करने और सामग्री प्रकार को एप्लिकेशन / json (मेरे डिफ़ॉल्ट) में बदलने में भी मदद करता है ।

एचटीएमएल स्रोत:

<form method="POST">
    <input type="text" name="startDate"/>
    <input type="text" name="endDate"/>
    <input type="text" name="startDate"/>
    <select name="reportTimeDetail">
        <option value="1">1</option>
    </select>
    <button type="submit"> Submit</button>
</form>  

दो इनपुट पाठ, एक का चयन और एक बटन तत्व के साथ एक सरल रूप।

जावास्क्रिप्ट पेज स्रोत:

<script type="text/javascript" src="JQuery 1.11.0 link"></script>
<script type="text/javascript">
    // File Download on form submition.
    $(document).on("ready", function(){
        $("form button").on("click", function (event) {
            event.stopPropagation(); // Do not propagate the event.

            // Create an object that will manage to download the file.
            new AjaxDownloadFile({
                url: "url that returns a file",
                data: JSON.stringify($("form").serializeObject())
            });

            return false; // Do not submit the form.
        });
    });
</script>  

बटन पर एक साधारण घटना। यह एक AjaxDownloadFile ऑब्जेक्ट बनाता है। AjaxDownloadFile वर्ग स्रोत नीचे है।

AjaxDownloadFile वर्ग स्रोत:

var AjaxDownloadFile = function (configurationSettings) {
    // Standard settings.
    this.settings = {
        // JQuery AJAX default attributes.
        url: "",
        type: "POST",
        headers: {
            "Content-Type": "application/json; charset=UTF-8"
        },
        data: {},
        // Custom events.
        onSuccessStart: function (response, status, xhr, self) {
        },
        onSuccessFinish: function (response, status, xhr, self, filename) {
        },
        onErrorOccured: function (response, status, xhr, self) {
        }
    };
    this.download = function () {
        var self = this;
        $.ajax({
            type: this.settings.type,
            url: this.settings.url,
            headers: this.settings.headers,
            data: this.settings.data,
            success: function (response, status, xhr) {
                // Start custom event.
                self.settings.onSuccessStart(response, status, xhr, self);

                // Check if a filename is existing on the response headers.
                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 = downloadUrl;
                        } else {
                            a.href = downloadUrl;
                            a.download = filename;
                            document.body.appendChild(a);
                            a.click();
                        }
                    } else {
                        window.location = downloadUrl;
                    }

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

                // Final custom event.
                self.settings.onSuccessFinish(response, status, xhr, self, filename);
            },
            error: function (response, status, xhr) {
                // Custom event to handle the error.
                self.settings.onErrorOccured(response, status, xhr, self);
            }
        });
    };
    // Constructor.
    {
        // Merge settings.
        $.extend(this.settings, configurationSettings);
        // Make the request.
        this.download();
    }
};

मैंने अपनी जेएस लाइब्रेरी में शामिल होने के लिए इस क्लास को बनाया। यह पुन: प्रयोज्य है। उम्मीद है की वो मदद करदे।


2
Blobऑब्जेक्ट IE10 + में समर्थित है।
क्रश

मुझे काम करने के responseTypeलिए arraybufferया इसके blobलिए xhr का सेट करना पड़ा । (अन्यथा, यह बहुत अच्छा काम करता है।)
tjklemz

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

यह एक महान जवाब है, लेकिन किसी कारण के लिए, मैं बस खाली पीडीएफ को तोड़ता रहता हूं। इसका पता नहीं लगा सकते। जब मैं एपीआई के माध्यम से एक ही बाइटसेट लौटाता हूं - यह ठीक है, तो यह एमवीसी प्रतिक्रिया के साथ कुछ करना है। मैं FileResult प्रतिक्रिया प्रकार का उपयोग करता हूं: फ़ाइल (बाइट्स, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
जरीज कस्तनोव्स

स्पष्टीकरण: यदि पता बार के माध्यम से यूआरएल को खोलें - फ़ाइल सही ढंग से खोली गई है। अगर मैं फ़ाइल प्राप्त करने के लिए AJAX + बूँद का उपयोग करता हूँ - फ़ाइल विकृत है।
जरीज कस्तनोव्स

7

मेरे लिए जो काम किया है वह निम्न कोड है, क्योंकि सर्वर फ़ंक्शन पुनर्प्राप्त कर रहा है File(memoryStream.GetBuffer(), "application/pdf", "fileName.pdf");:

$http.get( fullUrl, { responseType: 'arraybuffer' })
            .success(function (response) {
                var blob = new Blob([response], { type: 'application/pdf' });

                if (window.navigator && window.navigator.msSaveOrOpenBlob) {
                    window.navigator.msSaveOrOpenBlob(blob); // for IE
                }
                else {
                    var fileURL = URL.createObjectURL(blob);
                    var newWin = window.open(fileURL);
                    newWin.focus();
                    newWin.reload();
                }
});

इस टिप्पणी और नवीनतम क्रोम के समय में मेरे लिए यह पूरी तरह से काम करता है
लॉर्ड्रा एल

6

आप इस प्लगइन का उपयोग कर सकते हैं जो एक फॉर्म बनाता है, और इसे सबमिट करता है, फिर इसे पृष्ठ से हटा देता है।

jQuery.download = function(url, data, method) {
    //url and data options required
    if (url && data) {
        //data can be string of parameters or array/object
        data = typeof data == 'string' ? data : jQuery.param(data);
        //split params into form inputs
        var inputs = '';
        jQuery.each(data.split('&'), function() {
            var pair = this.split('=');
            inputs += '<input type="hidden" name="' + pair[0] +
                '" value="' + pair[1] + '" />';
        });
        //send request
        jQuery('<form action="' + url +
                '" method="' + (method || 'post') + '">' + inputs + '</form>')
            .appendTo('body').submit().remove();
    };
};


$.download(
    '/export.php',
    'filename=mySpreadsheet&format=xls&content=' + spreadsheetData
);

इसने मेरे लिए काम किया। इस प्लगइन यहाँ पाया


यह प्लगइन बस एक फॉर्म बनाता है, और इसे सबमिट करता है, फिर इसे पेज से हटा देता है। (अगर किसी को आश्चर्य हो रहा था)
क्रश करें

4

निम्नलिखित कोड ने मेरे लिए काम किया

//Parameter to be passed
var data = 'reportid=R3823&isSQL=1&filter=[]';
var xhr = new XMLHttpRequest();
xhr.open("POST", "Reporting.jsp"); //url.It can pdf file path
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.responseType = "blob";
xhr.onload = function () {
    if (this.status === 200) {
        var blob = new Blob([xhr.response]);
        const url = window.URL.createObjectURL(blob);
        var a = document.createElement('a');
        a.href = url;
        a.download = 'myFile.pdf';
        a.click();
        setTimeout(function () {
            // For Firefox it is necessary to delay revoking the ObjectURL
            window.URL.revokeObjectURL(data)
                , 100
        })
    }
};
xhr.send(data);

4

पीडीएफ की तरह स्ट्रीम डेटा प्राप्त करने के लिए पोस्ट अनुरोध में रिक्त पीडीएफ समस्या को ठीक करने के लिए, हमें अनुरोध प्रकार को प्रतिक्रिया में जोड़ना होगा 'अनुरोध' या 'बूँद'

$.ajax({
  url: '<URL>',
  type: "POST",
  dataType: 'arraybuffer',
  success: function(data) {
    let blob = new Blob([data], {type: 'arraybuffer'});
    let link = document.createElement('a');
    let objectURL = window.URL.createObjectURL(blob);
    link.href = objectURL;
    link.target = '_self';
    link.download = "fileName.pdf";
    (document.body || document.documentElement).appendChild(link);
    link.click();
    setTimeout(()=>{
        window.URL.revokeObjectURL(objectURL);
        link.remove();
    }, 100);
  }
});

3

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

इसका कारण इस प्रश्न के स्वीकृत उत्तर में बताया गया है : jQuery के पास AJAX अनुरोधों का उपयोग करके बाइनरी डेटा लोड करने के कुछ मुद्दे हैं, क्योंकि यह अभी तक कुछ HTML5 XHR v2 क्षमताओं को लागू नहीं करता है, इस वृद्धि के अनुरोध और इस चर्चा को देखें

तो HTMLHTTPRequestकोड का उपयोग करते हुए इस तरह दिखना चाहिए:

var req = new XMLHttpRequest();
req.open("POST", "URL", true);
req.responseType = "blob";
req.onload = function (event) {
    var blob = req.response;
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="name_for_the_file_to_save_with_extention";
    link.click();
}

2

एक छिपा iframe बनाएं, फिर ऊपर दिए गए अपने ajax कोड में:

यूआरएल: document.getElementById('myiframeid').src = your_server_side_url ,

और हटा दें window.open(response);


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

1
यह समाधान मूल पोस्ट की तरह ही GST अनुरोधों के लिए GET अनुरोधों के लिए काम करता है।
चिक्कोडोरो

2

यह स्निपेट कोणीय js उपयोगकर्ताओं के लिए है जो एक ही समस्या का सामना करेंगे, ध्यान दें कि प्रतिक्रिया फ़ाइल एक प्रोग्राम्ड क्लिक इवेंट का उपयोग करके डाउनलोड की जाती है। इस मामले में, हेडर को फ़ाइलनाम और सामग्री / प्रकार वाले सर्वर द्वारा भेजा गया था।

$http({
    method: 'POST', 
    url: 'DownloadAttachment_URL',
    data: { 'fileRef': 'filename.pdf' }, //I'm sending filename as a param
    headers: { 'Authorization': $localStorage.jwt === undefined ? jwt : $localStorage.jwt },
    responseType: 'arraybuffer',
}).success(function (data, status, headers, config) {
    headers = headers();
    var filename = headers['x-filename'];
    var contentType = headers['content-type'];
    var linkElement = document.createElement('a');
    try {
        var blob = new Blob([data], { type: contentType });
        var url = window.URL.createObjectURL(blob);

        linkElement.setAttribute('href', url);
        linkElement.setAttribute("download", filename);

        var clickEvent = new MouseEvent("click", {
            "view": window,
            "bubbles": true,
            "cancelable": false
        });
        linkElement.dispatchEvent(clickEvent);
    } catch (ex) {
        console.log(ex);
    }
}).error(function (data, status, headers, config) {
}).finally(function () {

});

कृपया अपने उत्तर के लिए कुछ स्पष्टीकरण लिखें।
गुफरान हसन

1

क्या आपको इसे अजाक्स के साथ करना है? यह एक iframe में लोड करने की संभावना नहीं होगी?


1
मैं जाँच कर रहा हूँ अगर, यह अजाक्स के साथ किया जा सकता है। यदि यह तकनीकी रूप से असंभव है या एक अवर दृष्टिकोण है, तो मैं अन्य दृष्टिकोणों पर स्विच करूंगा।
14

1

आशा है कि यह आपको कुछ घंटे बचाएगा और आपको सिरदर्द से बचाएगा। मुझे यह पता लगाने में थोड़ा समय लगा, लेकिन नियमित रूप से $ .ajax () अनुरोध करने से मेरी पीडीएफ फाइल बर्बाद हो गई, जबकि इसे एड्रेस बार के माध्यम से पूरी तरह से काम करने का अनुरोध किया। समाधान यह था:

Download.js शामिल करें: http://danml.com/download.html

तब $ .ajax () अनुरोध के बजाय XMLHttpRequest का उपयोग करें।

    var ajax = new XMLHttpRequest(); 

    ajax.open("GET", '/Admin/GetPdf' + id, true); 
    ajax.onreadystatechange = function(data) { 
        if (this.readyState == 4)
        {
            if (this.status == 200)
            {
                download(this.response, "report.pdf", "application/pdf");

            }
            else if (this.responseText != "")
            {
                alert(this.responseText);
            }
        }
        else if (this.readyState == 2)
        {
            if (this.status == 200)
            {
                this.responseType = "blob";
            }
            else
            {
                this.responseType = "text";
            }
        }
    };

    ajax.send(null);

0

var xhr;
var beforeSend = function(){
    $('#pleasewaitDL').modal('show');
}
$(function () {
    $('#print_brochure_link').click(function(){
        beforeSend();
        xhr = new XMLHttpRequest();
        xhr.open("GET",$('#preparedPrintModalForm').attr('action'), true); 
        xhr.responseType = "blob";
        xhr.onload = function (e) {
            if (this.status === 200) {
                var file = window.URL.createObjectURL(this.response);
                var a = document.createElement("a");
                a.href = file;
                a.download = this.response.name || "Property Brochure";
                console.log(file);
                document.body.appendChild(a);
                a.click();
                
                window.onfocus = function () {                     
                  document.body.removeChild(a)
                }
                $('#pleasewaitDL').modal('hide');
            };
        };
        xhr.send($('#preparedPrintModalForm').serialize());
    });
    $('#pleasewaitDLCancel').click(function() {
        xhr.abort();
    });
});


0

यदि आपको फ़ाइल-स्ट्रीम (इसलिए कोई भौतिक रूप से सहेजी गई PDF) के साथ काम नहीं करना है, जैसे हम करते हैं और आप पृष्ठ-पुनः लोड किए बिना पीडीएफ डाउनलोड करना चाहते हैं, तो निम्न फ़ंक्शन हमारे लिए काम करता है:

एचटीएमएल

<div id="download-helper-hidden-container" style="display:none">
     <form id="download-helper-form" target="pdf-download-output" method="post">
            <input type="hidden" name="downloadHelperTransferData" id="downloadHelperTransferData" />
     </form>
     <iframe id="pdf-helper-output" name="pdf-download-output"></iframe>
</div>

जावास्क्रिप्ट

var form = document.getElementById('download-helper-form');
$("#downloadHelperTransferData").val(transferData);
form.action = "ServerSideFunctionWhichWritesPdfBytesToResponse";
form.submit();

के कारण लक्ष्य = "पीडीएफ-डाउनलोड-उत्पादन" , प्रतिक्रिया iframe में लिखा है और इसलिए कोई पृष्ठ पुनः लोड निष्पादित किया जाता है, लेकिन पीडीएफ-प्रतिक्रिया धारा डाउनलोड के रूप में ब्राउज़र में उत्पादन होता है।


क्षमा करें, लेकिन आपको ट्रांसफ़रडेटा मान कैसे मिलेगा?
केट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.