Javascript XHR Download
https://codexpert.ro/blog/2013/11/04/fill-a-list-with-large-amount-of-data-part-2/
<a class=”download-link” title=”
Version 1 ” href=”https://codexpert.ro/blog/download/1807/?tmstv=1676620767″ rel=”nofollow”>
Virtual_List_Demo.zip (2192 downloads)
</a>
jQuery(function(e) { new DLM_XHR_Download }); class DLM_XHR_Download { constructor() { (dlmXHRinstance = this).init() } init() { dlmXHRinstance.attachButtonEvent() } attachButtonEvent() { jQuery("html, body").on("click", ".dlm-no-access-modal-overlay, .dlm-no-access-modal-close", function(e) { jQuery("#dlm-no-access-modal").remove() }), jQuery("html, body").on("click", "a", function(e) { const d = jQuery(this).attr("href"); if (jQuery(this).hasClass("dlm-no-xhr-download")) return !0; void 0 !== d && 0 <= d.indexOf(dlmXHRGlobalLinks) && dlmXHRinstance.handleDownloadClick(this, e) }) } handleDownloadClick(e, d) { d.stopPropagation(); var o = e.getAttribute("href"); let t = { button: e, href: o, buttonObj: jQuery(e) }; -1 === t.href.indexOf("blob:http") && "#" !== t.href && (d.preventDefault(), dlmXHRinstance.retrieveBlob(t)) } retrieveBlob(e) { let {button: n, href: l, buttonObj: a} = e, s; const i = new XMLHttpRequest , m = dlmXHR.prevent_duplicates , c = a.attr("target"); let u = a.attr("class"); u = void 0 !== u && "" !== u ? u.replace("dlm-download-started", "").replace("dlm-download-completed", "") : "", a.addClass("dlm-download-started"), n.setAttribute("href", "#"), n.removeAttribute("download"), n.setAttribute("disabled", "disabled"); e = '<img src="' + dlmXHRgif + '" class="dlm-xhr-loading-gif" style="display:inline-block; vertical-align: middle; margin-left:15px;">', n.innerHTML += e, e = 0 < l.indexOf("?") ? l + "&nonce=" + dlmXHR.nonce : l + "?nonce=" + dlmXHR.nonce; jQuery(document).trigger("dlm_download_triggered", [this, n, a, s]), i.responseType = "blob", i.onreadystatechange = function() { var {status: e, readyState: d, statusText: o} = i; let t = i.getAllResponseHeaders().split("\r\n").reduce((e,d)=>{ var [d,o] = d.split(": "); return e[d] = o, e } , {}) , r = "download"; if (void 0 !== t["dlm-file-name"] ? (r = t["dlm-file-name"].replace(/\"/g, "").replace(";", ""), r = decodeURI(r)) : void 0 !== t["content-disposition"] && (r = (r = t["content-disposition"].split("filename=")[1]).replace(/\"/g, "").replace(";", ""), r = decodeURI(r)), 2 === i.readyState) { if (void 0 !== t["dlm-no-waypoints"]) return i.abort(), void 0 !== t["dlm-redirect"] ? void (window.location.href = t["dlm-redirect"]) : void (window.location.href = l); if (void 0 !== t["dlm-external-download"]) return i.abort(), void dlmXHRinstance.dlmExternalDownload(t, n, a, r, l); if (0 === Object.keys(t).filter(e=>-1 !== e.indexOf("dlm-")).length) return i.abort(), void (window.location.href = l); if (void 0 !== t["dlm-no-access"] && "true" === t["dlm-no-access"] && void 0 !== t["dlm-no-access-modal"] && 0 != t["dlm-no-access-modal"]) return dlmXHRinstance.dlmNoAccessModal(t), n.removeAttribute("download"), n.setAttribute("href", l), a.removeClass().addClass(u).find("span.dlm-xhr-progress").remove(), a.find(".dlm-xhr-loading-gif").remove(), void i.abort(); if (void 0 !== t["dlm-error"] && "" !== t["dlm-error"] && null !== t["dlm-error"]) return dlmXHRinstance.dlmLogDownload(t, "failed", !1), n.removeAttribute("download"), n.setAttribute("href", l), a.removeClass().addClass(u).find("span.dlm-xhr-progress").remove(), a.find(".dlm-xhr-loading-gif").remove(), i.abort(), void (void 0 !== t["dlm-no-access-modal"] && 0 != t["dlm-no-access-modal"] ? dlmXHRinstance.dlmNoAccessModal(t["dlm-download-id"], t["dlm-version-id"], t["dlm-no-access-modal-text"]) : a.append('<span class="dlm-xhr-error">' + t["dlm-error"] + "</span>")); if (void 0 !== t["dlm-redirect"] && "" !== t["dlm-redirect"] && null !== t["dlm-redirect"]) return dlmXHRinstance.dlmLogDownload(t, "redirected", !1, t["dlm-redirect"], t["dlm-no-access"], c), n.removeAttribute("download"), n.setAttribute("href", l), a.removeClass().addClass(u).find("span.dlm-xhr-progress").remove(), a.find(".dlm-xhr-loading-gif").remove(), void i.abort() } if (404 == e && 2 == d) { let e = document.createElement("p"); e.innerHTML = o, n.parentNode.appendChild(e) } if (401 == e && 2 == d) window.location.href = o; else { if (403 == e && 2 == d) { let e = document.createElement("p"); e.innerHTML = o, n.parentNode.appendChild(e) } 200 == e && 4 == d && (o = i.response, s = URL.createObjectURL(o), n.removeEventListener("click", dlmXHRinstance.handleDownloadClick), n.setAttribute("download", "" + r), n.setAttribute("href", s), n.click(), a.removeClass().addClass(u + " dlm-download-complete"), dlmXHRinstance.attachButtonEvent(), jQuery(document).trigger("dlm_download_complete", [this, n, a, s]), dlmXHRinstance.dlmLogDownload(t, "completed", m), window.URL.revokeObjectURL(s), n.removeAttribute("download"), n.setAttribute("href", l), a.find(".dlm-xhr-loading-gif").remove(), setTimeout(function() { a.removeClass().addClass(u).find("span.dlm-xhr-progress").remove() }, 4e3)) } } , i.addEventListener("progress", function(e) { let d = e.loaded / e.total * 100; d = d.toFixed(); var o; a.find("span.dlm-xhr-progress").remove(), o = "dlm-download-started download-" + 10 * Math.ceil(d / 10), 1 / 0 != d && a.append('<span class="dlm-xhr-progress"> ' + d + "%</span>"), a.removeClass().addClass(u + " " + o), jQuery(document).trigger("dlm_download_progress", [this, n, a, s, e, d]) }), i.onerror = function() { n.removeAttribute("download"), n.setAttribute("href", l), a.removeClass().addClass(u + " dlm-no-xhr-download").find("span.dlm-xhr-progress").remove(), a.append('<span class="dlm-xhr-error">' + dlmXHRtranslations.error + "</span>"), console.log("** An error occurred during the transaction") } , i.open("GET", e, !0), i.setRequestHeader("dlm-xhr-request", "dlm_XMLHttpRequest"), i.send() } dlmLogDownload(e, d, o, t=null, r=null, n="_self") { null !== r ? window.location.href = t : (r = window.location.href, d = { download_id: e["dlm-download-id"], version_id: e["dlm-version-id"], status: d, cookie: o, currentURL: r, action: "log_dlm_xhr_download", responseHeaders: e, nonce: dlmXHR.nonce }, jQuery.post(dlmXHR.ajaxUrl, d, function(e) { null !== t && (null == n && (n = "_self"), window.open(t, n)) })) } dlmNoAccessModal(e) { let d = "empty-download" , o = "empty-version" , t = "empty-restriction" , r = ""; void 0 !== e["dlm-download-id"] && (d = e["dlm-download-id"]), void 0 !== e["dlm-version-id"] && (o = e["dlm-version-id"]), void 0 !== e["dlm-no-access-modal-text"] && (r = e["dlm-no-access-modal-text"]), void 0 !== e["dlm-no-access-restriction"] && (t = e["dlm-no-access-restriction"]); const n = { download_id: d, version_id: o, modal_text: r, restriction: t, action: "no_access_dlm_xhr_download", nonce: dlmXHR.nonce }; jQuery.post(dlmXHR.ajaxUrl, n, function(e) { jQuery("#dlm-no-access-modal").remove(), jQuery("body").append(e), jQuery(document).trigger("dlm_no_access_modal_response", [e, n]) }) } dlmExternalDownload(e, t, r, n, l) { const a = new XMLHttpRequest , d = e["dlm-external-download"]; r.attr("target"); let s = r.attr("class"), i; s = void 0 !== s && "" !== s ? s.replace("dlm-download-started", "").replace("dlm-download-completed", "") : "", r.addClass("dlm-download-started"), t.setAttribute("href", "#"), t.removeAttribute("download"), t.setAttribute("disabled", "disabled"), jQuery(document).trigger("dlm_download_triggered", [this, t, r, i]), a.responseType = "blob", a.onreadystatechange = function() { var {status: e, readyState: d} = a , o = a.getAllResponseHeaders().split("\r\n").reduce((e,d)=>{ var [d,o] = d.split(": "); return e[d] = o, e } , {}); if (403 === e) return dlmXHRinstance.dlmLogDownload(o, "failed", !1), a.abort(), void r.append('<span class="dlm-xhr-error">Acces Denied to file.</span>'); 200 == e && 4 == d && (e = a.response, i = URL.createObjectURL(e), t.removeEventListener("click", dlmXHRinstance.handleDownloadClick), t.setAttribute("download", "" + n), t.setAttribute("href", i), t.click(), r.removeClass().addClass(s + " dlm-download-complete"), dlmXHRinstance.attachButtonEvent(), jQuery(document).trigger("dlm_download_complete", [this, t, r, i]), dlmXHRinstance.dlmLogDownload(o, "completed", !1), window.URL.revokeObjectURL(i), t.removeAttribute("download"), t.setAttribute("href", l), r.find(".dlm-xhr-loading-gif").remove(), setTimeout(function() { r.removeClass().addClass(s).find("span.dlm-xhr-progress").remove() }, 1e3)) } , a.addEventListener("progress", function(e) { let d = e.loaded / e.total * 100; d = d.toFixed(); var o; r.find("span.dlm-xhr-progress").remove(), o = "dlm-download-started download-" + 10 * Math.ceil(d / 10), 1 / 0 != d && r.append('<span class="dlm-xhr-progress"> ' + d + "%</span>"), r.removeClass().addClass(s + " " + o), jQuery(document).trigger("dlm_download_progress", [this, t, r, i, e, d]) }), a.onerror = function() { t.removeAttribute("download"), t.setAttribute("href", l), r.removeClass().addClass(s + " .dlm-no-xhr-download").find("span.dlm-xhr-progress").remove(), r.append('<span class="dlm-xhr-error">' + dlmXHRtranslations.error + "</span>"), console.log("** An error occurred during the transaction") } , a.open("GET", d, !0), a.setRequestHeader("dlm-xhr-request", "dlm_XMLHttpRequest"), a.send() } }
Triggering a File Download from an XHR Post Request
July 07, 2018 | 1 minute read
If you need your webapp to offer a file download for an asset created on the fly, like say a PDF generated from content entered by a user, how do you do that? This was a puzzle I encountered not too long ago on a personal proejct. I dug around and found a pretty solid solution, which I’ll detail in this post.
Photo by Mandi Cai
The specific scenario in this personal project was that I needed to POST data from an HTML form to a server, and then trigger a download for the payload of the POST response. There might be a few reasons you would want to do this kind of trickery, but for me, I was generating a PDF on the server based on the contents of te POST request, and then returning that PDF to the client. The desired experience for the user is just a simple ‘Download’ button, so even though an asset is being dynamically generated, we want to give the illusion that they’re just downloading.
The Request
Step one is to set up the XHR request in Javascript. For some context, let’s imagine a simple HTML form:
<form id="form">
<input type="text"/>
<button type="submit" form="form">Generate Report</button>
</form>
When this form is submitted, we’re going to send the form data to the server, and expect a PDF back in return, generated dynamically, which is where the responseType comes in.
let xhr = new XMLHttpRequest();
//set the request type to post and the destination url to '/convert'
xhr.open('POST', 'convert');
//set the reponse type to blob since that's what we're expecting back
xhr.responseType = 'blob';
let formData = new FormData(this);
xhr.send(formData);
What Is a Blob?
“A Blob object represents a file-like object of immutable, raw data. Blobs represent data that isn’t necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user’s system.” — MDN Web Docs: Blob
The Response
Great now that we’ve wired up the submission, let’s take a bit of time to talk about what’s happening on the server’s side. I’m keeping this post backend agnostic, so I won’t be discussing specific syntax. However, the basic mechanics are pretty simple. The server will accept the incoming POST request, and parse out the form data. It will then use that form data to generate some type of file, such as a PDF, represented in memory as Binary data. You’ll pipe that binary data back to the user as the response to the POST request. Remember that on the client side, we’ve set the request to expect a Blob.
With that in mind, it’s time to focus on what to do for the response. For this, we’ll make use of the request’s onload function.
xhr.onload = function(e) {
if (this.status == 200) {
// Create a new Blob object using the
//response data of the onload object
var blob = new Blob([this.response], {type: 'image/pdf'});
//Create a link element, hide it, direct
//it towards the blob, and then 'click' it programatically
let a = document.createElement("a");
a.style = "display: none";
document.body.appendChild(a);
//Create a DOMString representing the blob
//and point the link element towards it
let url = window.URL.createObjectURL(blob);
a.href = url;
a.download = 'myFile.pdf';
//programatically click the link to trigger the download
a.click();
//release the reference to the file by revoking the Object URL
window.URL.revokeObjectURL(url);
}else{
//deal with your error state here
}
};
All Together Now
Finally, let’s combine all of this together into a single event listener that is fired with the form is submitted.
document.querySelector('#form').addEventListener('submit', (e)=>{
e.preventDefault();
let xhr = new XMLHttpRequest();
//set the request type to post and the destination url to '/convert'
xhr.open('POST', 'convert');
//set the reponse type to blob since that's what we're expecting back
xhr.responseType = 'blob';
let formData = new FormData(this);
xhr.send(formData);
xhr.onload = function(e) {
if (this.status == 200) {
// Create a new Blob object using the response data of the onload object
var blob = new Blob([this.response], {type: 'image/pdf'});
//Create a link element, hide it, direct it towards the blob, and then 'click' it programatically
let a = document.createElement("a");
a.style = "display: none";
document.body.appendChild(a);
//Create a DOMString representing the blob and point the link element towards it
let url = window.URL.createObjectURL(blob);
a.href = url;
a.download = 'myFile.pdf';
//programatically click the link to trigger the download
a.click();
//release the reference to the file by revoking the Object URL
window.URL.revokeObjectURL(url);
}else{
//deal with your error state here
}
};
});
南来地,北往的,上班的,下岗的,走过路过不要错过!
======================个性签名=====================
之前认为Apple 的iOS 设计的要比 Android 稳定,我错了吗?
下载的许多客户端程序/游戏程序,经常会Crash,是程序写的不好(内存泄漏?刚启动也会吗?)还是iOS本身的不稳定!!!
如果在Android手机中可以简单联接到ddms,就可以查看系统log,很容易看到程序为什么出错,在iPhone中如何得知呢?试试Organizer吧,分析一下Device logs,也许有用.
对于博客园里的网友,不敢称为叫”程序员”的人,你们攻击性太强,看来你们是不会想到我的用意的.园子里有不少人都非常喜欢Jeffrey,是因为它的第一版 框架设计 CLR via C#.
可是从第一版到现在的第三版,没有看到真正底层的东西,内容仅仅是比MSDN文档更丰富一些,可能是我的要求太高了吧.
也就是因为它很多时候会接触到微软开发人员,会经常聊聊某些问题而已,而它又将这些问题反应到书中.也许它就像一个小记者.
它的年龄大我们不多,我的孩子与它小儿子一般大,如果我能向它那样出入微软与它们开发人员长时间交流,不仅仅会牛成它这样…..
可是微软的开发人员不会扔太多时间在它这儿的.所以它会整天追着这个,赶它那个..屁颠个不停吧…
而它的另一版被称为好书的 Windows核心编程,更是没有什么深度可言,仅仅是将windows提供的api,以及内核功能再重申了一遍.
这些书对晋及编程知识是有些贡献的,再说一遍我不是在匾低谁,说说想法而已.