Javascript binary file download, and ajax file POST upload in Chrome Extension - javascript

I'm writing a chrome extension content script which will embed itself on some pages, and when there are certain file type links (.doc, .torrent, etc) it will download that file, and then do a file POST to a python web server which will save that file. The python server is working, and handles a normal multipart/form-data POST request, and successfully saves the file when I use the html interface I wrote for it.
I have javascript downloading the file properly:
var req = new XMLHttpRequest();
req.open('GET', 'http://foo.com/bar.torrent', false);
req.overrideMimeType('text/plain; charset=x-user-defined');
req.send(null);
if (req.status != 200) return '';
var response = req.responseText;
And then when I try to create a POST request and upload it
// Define a boundary, I stole this from IE but you can use any string AFAIK
var boundary = "---------------------------7da24f2e50046";
var xhr = new XMLHttpRequest();
var body = '--' + boundary + '\r\n'
// Parameter name is "file" and local filename is "temp.txt"
+ 'Content-Disposition: form-data; name="upfile";'
+ 'filename="temp.torrent"\r\n'
// Add the file's mime-type
+ 'Content-type: application/bittorrent\r\n\r\n'
+ response + '\r\n';
//+ boundary + '--';
xhr.open("POST", "http://python.server/", true);
xhr.setRequestHeader(
"Content-type", "multipart/form-data; boundary="+boundary
);
xhr.onreadystatechange = function ()
{
if (xhr.readyState == 4 && xhr.status == 200)
alert("File uploaded!");
}
xhr.send(body);
It thinks that it uploaded successfully, but when I try to open the file it says the data is corrupted. I think this is some kind of encoding issue, but I'm not 100% sure.
Any thoughts would be very helpful.

Your upload method does not work, because all binary characters are encoded as UTF-8. I posted the explanation and solution in an answer at this question.
In your case, you don't need to manually create the post data. Request the initial data in a smart way, and use the FormData object to post the binary data. For instance:
var x = new XMLHttpRequest();
x.onload = function() {
// Create a form
var fd = new FormData();
fd.append("upfile", x.response); // x.response is a Blob object
// Upload to your server
var y = new XMLHttpRequest();
y.onload = function() {
alert('File uploaded!');
};
y.open('POST', 'http://python/server/');
y.send(fd);
};
x.responseType = 'blob'; // <-- This is necessary!
x.open('GET', 'http://foo.com/bar.torrent', true);
x.send();
Note: I replaced false with true at the initial request. Avoid using synchronous XMLHttpRequest when it's also possible to asynchronously create the request.
If you don't understand the answer, here are more examples with thorough explanations:
XMLHttpRequest: Multipart/Related POST with XML and image as payload - FormData is not used, but the post data is manually created instead.
Upload a File in a Google Chrome Extension - A sample Chrome extension which uses Web Workers (with a FormData polyfill) to upload files
Google chrome rehost image extension - Scrapes an image from the page, and upload the image to imgur using a Chrome extension.

Related

How to download file into memory without writing the file to the system or without creating the file in a directory using javascript

I am looking for something like this, but using javascript. I want to download the file into javascript memory and then add the file content to a file input so that I can upload that again. I have written a function to access the file.
function fileDownload(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.send();
xhr.onload = function() {
if (xhr.status != 200) { // analyze HTTP status of the response
console.log(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found
} else { // show the result
callback(xhr.response)
}
};
}
So xhr supports the response type as text, arraybuffer, blob and document. So which type should I use? I want to support all types of files. Can I use blob?In that case how can I assign it to an input type of file. Please help.

Image Upload Issue using gcs Upload Signed URL | React JS

Using this for to generate GCS Upload signed url v4
https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/storage/cloud-client/storage_generate_upload_signed_url_v4.py#L27
I have generated upload signed url using this function "generate_upload_signed_url_v4" as mentioned above and also I tried using GSUTIL as well using
gsutil signurl -m PUT service_account.json gs://<bucket>/file.png
I am working on a demo where I need to upload PDF, PNG to GCS bucket. File uploading is file. But when I preview file in GCS Storage Console and download/preview file form Link URL
PDF is fine. But PNG somehow got damaged and not opening/previewing.
I am using Chrome '81.0.4044.138'
When I further preview PNG file using text editor it contains some header content at the top of file.
i.e
------WebKitFormBoundarysZ3BDVaNOhqwENsp
Content-Disposition: form-data; name="file"; filename="test.png"
Content-Type: image/png
So if we remove this from top of file the file will open fine..
I have created a sample React project which can be accessed here
Demo: https://github.com/qaisershehzad/upload-gcs
I am using this code for file upload
` const url = "https://storage.googleapis.com//file.png.png?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=gitlab-ci%.iam.gserviceaccount.com%2F20200521%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20200521T175304Z&X-Goog-Expires=36000&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature="
const data = new FormData()
data.append('file', this.state.selectedFile)
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader("Content-type", "application/octet-stream");
xhr.onload = function (response) {
console.log('on-load', response);
};
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log("Status OK")
} else {
console.log("Status not 200")
}
}
};
xhr.onerror = function (response) {
console.log("Response error", response)
};
xhr.upload.onprogress = function (evt) {
// For uploads
if (evt.lengthComputable) {
var percentComplete = parseInt((evt.loaded / evt.total) * 100);
console.log("progress", percentComplete)
}
}
xhr.send(data);`
I also tried same thing on Android as well and facing same issue. In Png at top these header string got added which not allowing to open Png file.
Png uploading is working fine using this CURL request.
curl -X PUT -H 'Content-Type: application/octet-stream' --upload-filen file.png 'https://storage.googleapis.com//file.png?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=gitlab-ci%.iam.gserviceaccount.com%2F20200521%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20200521T175304Z&X-Goog-Expires=36000&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature='
It will be helpful if someone can help to resolve this issue.
Actually you are sending FormData which causes a problem, you will see file get uploaded but when you retrieve file its content will be different in case of image possibly image will not get rendered.
The reason why cURL is working, because you are directly sending file not FormData.
To fix this scenario directly pass the file to ajax like mentioned below:
const file = this.state.selectedFile;
ajax.setRequestHeader('Content-Type', file.type);
ajax.send(file); // direct file not FormData

Send pdf via express to js client and download

I am trying to send pdf file from my express server using the client when requested like this:
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'attachment; filename=test.pdf');
fs.createReadStream('test.pdf').pipe(res);
And then on the client side I am trying to download it by taking the resulting string converting it to a url and downloading from there.
var blob = new Blob[pdfString], { type: 'application/pdf' });
var url = window.URL;
var downloadUrl = url.createObjectURL(blob);
However the resulting file is two empty pages and I beleive think it might be because the resulting file is url is to large? If anyone could figure out whats wrong here or tell me of a better way to do it that would be awesome.
I was able to figure it out using XMlHttpRequest:
var req = new XMLHttpRequest();
req.open('GET', path , true);
req.responseType = "arraybuffer";
req.onload = (event) => {
downloadPdf(req.response); //This is where I convert to blob
}
req.send();

Post complex data using ajax and open returned PDF in new window

I need to use JQuery ajax to post a complex and sensitive data object (nested objects, arrays, and Personally Identifiable Information) to my server, where a PDF is generated and returned to the client. The client browser then should open the PDF in a new window.
Because of the nature of the data the request neither can nor should be an encoded URL - it must include the data as a JSON body.
The other questions/answers on this subject did not solve the problem in my case or do not do so completely.
Solution
POST with the data in the body as JSON.
Set the expected Content-Type of the response to arraybuffer (on the client and server).
When the request has complete successfully, convert the response to a Blob.
Create an object url to the Blob and open it in a new window.
Notes
JQuery ajax does not support the arraybuffer Content-Type so the base JavaScript xhr must be used (if you don't have any other options).
Internet Explorer has its own functionality for handling and displaying Blob's, so a special case is needed.
Supported browsers does not include IE9
Code
RequestPdf = function (url, data) {
var request = new XMLHttpRequest(), file, fileURL;
request.open("POST", url);
request.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
request.responseType = "arraybuffer";
request.send(data);
request.onreadystatechange = function () {
if (request.readyState === 4 && request.status === 200) {
file = new Blob([request.response], { type: 'application/pdf' });
if (window.navigator && window.navigator.msSaveOrOpenBlob) { // IE
window.navigator.msSaveOrOpenBlob(file);
} else {
fileURL = URL.createObjectURL(file);
window.open(fileURL);
}
}
};
};

xhr.send(file) doesn't post it as multipart

On Firefox 3.6 and Chrome, using xhr.send(file) just puts the raw contents into the body of the request and it is not a true multipart/form-data upload.
Tried doing this: http://kaply.com/weblog/2010/05/20/post-multipart-form-xhr/
But, can't really mix string with File contents during send().
Any workarounds?
xhr.sendAsBinary() is non-standard. Instead, use xhr.send(FormData), which does create a multipart/form-data request, allows appending files, and arbitrary form data.
var formData = new FormData();
formData.append(file.name, file);
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload', true);
xhr.onload = function(e) { ... };
xhr.send(formData); // multipart/form-data
See http://www.html5rocks.com/en/tutorials/file/xhr2/#toc-send-formdata
The key thing is using sendAsBinary(body) istead of send(body). See the last comment on the page you linked!

Categories