jquery ajax not serving pdf file - javascript

I am trying to download a pdf file using jquery, ajax & django.
My django views.py:
if request.POST.get('action') == 'download_labels':
order_list = json.loads(request.POST.get('order_dict'), None)
PackedOrders(dbname, order_list).downloadLabels()
file = open('shipping_labels.pdf','rb')
response = HttpResponse(file, content_type='application/pdf')
response['Content-Disposition'] = "attachment; filename=shipping_labels.pdf"
os.system('rm shipping_labels.pdf')
return HttpResponse(response, content_type='application/pdf')
My ajax query:
data : {action:'download_labels',
order_dict:JSON.stringify($checkedRows)},
success : function(response, status, request) {
var file = new Blob([response], {type: 'application/pdf'});
var fileURL = window.URL.createObjectURL(file);
window.open(fileURL,'_blank');
},
The ajax returns the file as binary data response and open it in a new tab. But all I see in new tab are blank pages. The number of blank pages is equal to number of pages in original pdf file.
In Console I see this:
Error: Invalid XRef stream header
...
Warning: Indexing all PDF objects
pdf.worker.js (line 235)
<System>
PDF 85b859244e496561d60d217869d5d38a [1.3 - / -] (PDF.js: 1.3.76)
Error: Bad FCHECK in flate stream: 120, 239
...
Here is the complete log file.

I'm not a jQuery expert but I do not think jQuery ajax supports blobs. In the documentation it only lists these data types: xml, json, script, or html.
However I was able to get this functionality to work without using jQuery and using ajax with plain JavaScript with this code,
My JavaScript (I would also add error handling to this)
var xhr = new XMLHttpRequest();
xhr.open('GET', '/pdf_test', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) {
var blob = new Blob([this.response], {type: 'application/pdf'}),
fileURL = URL.createObjectURL(blob);
window.open(fileURL,'_blank');
}
};
xhr.send();
My django view ( I would also add error handling to this)
def pdf_test(request):
pdf_file = open(r'C:\Pdfs\calculation_of_semiconductor_failure_rates.pdf', 'rb')
response = HttpResponse(pdf_file, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="shippinglabels.pdf"'
return response
On top of this if you don't need to open in new tab but can just download the file, then you can avoid ajax/Javascript entirely and just use HTML which is a simpler approach
<a id="pdf-test" href="/pdf_test">Download PDF</a>
For credit and further reading I used these links
jQuery Ajax
StackOverflow question
Introduction to JavaScript Blobs and File Interface

Related

Rss will not parse

I've been experimenting with xml data but I found an xml file with a structure that I've never seen before. I tried to call it using php and log it in the console but no luck any idea as to why? When I try this method with other files there seems to be no issue, for example, if you replace the url with this one "http://news.google.com/news?ned=us&topic=h&output=rss" it works fine. Code is below
PHP
$xml = "https://w1.weather.gov/xml/current_obs/display.php?stid=KATL";
echo file_get_contents($xml);
JS
var xhr = new XMLHttpRequest();
xhr.open("GET", "metar.php");
xhr.onload = function (response) {
var res = this.response;
console.log(res);
}
xhr.send();
This is not a problem with your script, but with the resource you are requesting. The web server is returning the "forbidden" status code.
You should probably check the https and the url.

Javascript send video blob to PHP - how to also send mimetype?

I'm generating a blob in JavaScript via a recorded video stream (MediaRecorder).
The resultant file ends up as a .webm, as confirmed by ffmpeg. So far, so good. Here's what I'm doing.
//promises container
let dfds = [];
//promise 1 - get blob file content
dfds.push(new Promise(resolve => {
var xhr = new XMLHttpRequest();
xhr.open('GET', file_url, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) resolve(this.response);
};
xhr.send();
}));
//(other non-pertinent promises omitted here)
//when ready, build and send request
Promise.all(dfds).then(resolution_data => {
let req = new XMLHttpRequest(), fd = new FormData();
fd.append('title', title);
fd.append('file', resolution_data[0]); //<-- the blob
req.open('POST', 'my-script.php');
req.send(fd);
});
This works perfectly. However, on the PHP side, when I run print_r($_FILES), the mime type is ending up as text/plain. I'd like to submit to PHP the mime type, so I can check this before allowing the file through (I'm aware mimetype is not always reliable, but it's just another check of several I'm doing.)
I added this to the AJAX request:
req.setRequestHeader('Content-Type', 'video/webm');
However with this added, the PHP script reports that $_FILES is completely empty.
How can I submit the mime type along with the file?
The formData.append() as a 'filename' field. See:
https://developer.mozilla.org/en-US/docs/Web/API/FormData/append
What would happen if you gave it a name like 'myMovie.webm'? It's worth a try, I think. So:
fd.append('file', resolution_data[0]);
would become:
fd.append('file', resolution_data[0], 'myMovie.webm');
I haven't tested this at all, it's just a suggestion.
Since you haven't reacted yet, I read a bit more. I also found this:
https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects
in it they use this code:
var content = '<a id="a"><b id="b">hey!</b></a>';
var blob = new Blob([content], { type: "text/xml"});
formData.append("webmasterfile", blob);
Note the mime type! That looks very promising!

Google Drive API: Correct way to upload binary files via the Multipart API

I'm trying to upload a binary file to Google Drive via the
multipart upload API v3.
Here's the hex representation of the content of the file:
FF FE
For some reason the above content gets encoded as UTF-8 (I assume)
when I try to POST it, enclosed in a multipart payload:
--BOUNDARY
Content-Type: application/json
{"name": "F.ini"}
--BOUNDARY
Content-Type: application/octet-stream
ÿþ <-- in the outbound request, this gets UTF-8 encoded
--BOUNDARY--
Hex representation of the file that ultimately gets stored on server side:
C3 BF C3 BE
The problem only occurs in the sending stage:
if I check the length of the content read from the file I always get 2;
regardless of whether I use FileReader#readAsBinaryString or FileReader#readAsArrayBuffer
(producing a string with length 2, and an ArrayBuffer with byteLength 2, respectively).
Here's the minimal code that I'm using to generate the multipart payload:
file = picker.files[0]; // 'picker' is a file picker
reader = new FileReader();
reader.onload = function (e) {
content = e.target.result;
boundary = "BOUNDARY";
meta = '{"name": "' + file.name + '"}';
console.log(content.length); // gives 2 as expected
payload = [
"--" + boundary, "Content-Type: application/json", "", meta, "", "--" + boundary,
"Content-Type: application/octet-stream", "", content, "--" + boundary + "--"
].join("\r\n");
console.log(payload.length); // say this gives n
xhr = new XMLHttpRequest();
xhr.open("POST", "/", false);
xhr.setRequestHeader("Content-Type", "multipart/related; boundary=" + boundary);
xhr.send(payload); // this produces a request with a 'Content-Length: n+2' header
// (corresponding to the length increase due to UTF-8 encoding)
};
reader.readAsBinaryString(file);
My question is twofold:
Is there a way to avoid this automatic UTF-8 encoding? (Probably not, because
this answer
implies that the UTF-8 encoding is part of the XHR spec.)
If not, what is the correct way to "inform" the Drive API that my file content is UTF-8 encoded?
I have tried these approaches, with no success:
appending ; charset=utf-8 or ; charset=UTF-8 to the binary part's Content-Type header
doing the same to the HTTP header on the parent request
(Content-Type: multipart/related; boundary=blablabla, charset=utf-8;
also tried replacing the comma with a semicolon)
I need the multipart API because AFAIU the "simple" API
does not allow me to upload into a folder
(it only accepts a filename as metadata, via the Slug HTTP header,
whereas the JSON metadata object in the multipart case allows a parent folder ID to be specified as well).
(Just thought of mentioning this because the "simple" API handles things correctly
when I directly POST the File (from the picker) or ArrayBuffer (from FileReader#readAsArrayBuffer) as the XHR's payload.)
I do not want to utilize any third-party libraries because
I want to keep things as light as possible, and
keeping aside reinventing-the-wheel and best-practices stuff, anything that is accomplished by a third party library should be doable via plain JS as well (this is just a fun exercise).
For the sake of completeness I tried uploading the same file via the GDrive web interface, and it got uploaded just fine;
however the web interface seems to base64-encode the payload, which I would rather like to avoid
(as it unnecessarily bloats up the payload, esp. for larger payloads which is my eventual goal).
How about this modification?
Modification points:
Used new FormData() for creating the multipart/form-data.
Used reader.readAsArrayBuffer(file) instead of reader.readAsBinaryString(file).
Send the file as a blob. In this case, the data is sent as application/octet-stream.
Modified script:
file = picker.files[0]; // 'picker' is a file picker
reader = new FileReader();
reader.onload = function (e) {
var content = new Blob([file]);
var meta = {name: file.name, mimeType: file.type};
var accessToken = gapi.auth.getToken().access_token;
var payload = new FormData();
payload.append('metadata', new Blob([JSON.stringify(meta)], {type: 'application/json'}));
payload.append('file', content);
xhr = new XMLHttpRequest();
xhr.open('post', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart');
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
xhr.onload = function() {
console.log(xhr.response);
};
xhr.send(payload);
};
reader.readAsArrayBuffer(file);
Note:
In this modified script, I put the endpoint and the header including the access token. So please modify this for your environment.
In this case, I used a scope of https://www.googleapis.com/auth/drive.
Reference:
Using FormData Objects
In my environment, I could confirmed that this script worked. But if this didn't work in your environment, I'm sorry.

Converting data of content type 'application/java-archive' into blob object

I have one GET request which returns a JAR file, which I need to download.
Easiest way to to the downloading part is to let browser handle the downloading part as follows :
window.location.href = URL
But, that does not work for error handling (handling server errors).
Alternatively, I found another way which creates blob object, and creates Object URL from that object and assigns it to anchor tag for downloading -
callSomeURL().then((res) => {
const blob = new Blob([res._body], { type: res.headers.get('content-type')
});
const url = window.URL.createObjectURL(blob);
const linkElement = document.createElement('a');
linkElement.setAttribute('href', url);
linkElement.setAttribute('download', 'test jar file');
const clickEvent = new MouseEvent('click', {view: window});
linkElement.dispatchEvent(clickEvent);
}).catch((error) => error handling part)
content type is 'application/java-archive'
res._body is encoded string.
Problem with this is that the file downloaded is invalid / corrupt. (Not sure whats wrong with the implementation).
I have 2 questions here -
What is the proper way to create a blob object? Is it created using the encoded data that the response returns or the URL of the request?
What is the proper way to handle file download with error handling?
Your problem is that the response you get is plain/text.
So when you generate your Blob, it's still plain text, and the binary data got mangled when converted to utf16 by javascript.
To avoid that, you can request it as a blob directly from your ajax call.
With XMLHttpRequest, you can set the responseType parameter of your XHR object:
var xhr = new XMLHttpRequest();
xhr.open('get', your_url);
xhr.responseType = 'blob';
xhr.onload = function() {
var blob = xhr.response;
// do something with the blob
var url = URL.createObjectURL(blob);
anchor.href = url
anchor.click();
};
xhr.send();
With fetch API, you can call the blob() method of the Response object
fetch(your_url)
.then(resp => resp.blob())
.then(doSomethingWithBlob)

Trying to upload file stored using HTML 5 File API

I'm piecing together tutorials from the web to be able to build a tool where users can upload images offline in an HTML5 app to filesystem storage along with some personal details and when they are online, they can "sync" which uploads the files and their details to the server.
I've managed to get a simple page up that stores images in file storage & sizes them down but I am unable to figure out how to post them using XMLHttpRequest. I've managed to push just the file data and store it one by one by using php://input (taken from Upload file from HTML5 Filesystem by XMLHttpRequest) but I need it to be uploaded as a form field that I can retrieve via $_FILES.
This function in particular:
function (fileName, successFct) {
getFileSystem(function (fileSystem) {
var fd = new FormData();
var xhr = new XMLHttpRequest();
xhr.open('POST', 'upload.php', true);
xhr.onload = function(e) {
if (this.status == 200) {
console.log(this.responseText);
}
};
fileSystem.root.getFile(fileName, {}, function (fileEntry) {
fileEntry.file(function(file) {
fd.append('file' + i, file);
fd.append('name' + i, 'name' + i);
});
}, errorFct);
xhr.send(fd);
}
);
};
Full code can be seen # http://pastebin.com/W0x9q6YH
In upload.php if I do the following
print_r($_FILES);
print_r($_POST);
It just shows two empty arrays.
Struggling with a similar problem: one thing I noticed about your code, you do not set your Content-Type header to multipart/form-data.
I do not have a working sample yet, but I'm pretty sure to use FormData, you need that old multipart/form-data magic.

Categories

Resources