blob in multipart/form-data sending as [object Blob] with XMLHttpRequest - javascript

I have a binary file I want to send in a multipart/form-data POST request. I want to include the binary in my javascript so I found a javascript function to convert a base64 string to a blob, it is below.
var b64data = 'blablabla';
const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, {type: contentType});
return blob;
}
const contentType = 'application/x-gzip';
const payload = b64toBlob(b64data, contentType);
I then use that blob as part of a multipart/form-data send with XMLHttpRequest. The relevant code is:
function fileUpload(url, fileData, fileName, nameVar, ctype) {
var fileSize = fileData.length,
boundary = "ABCDEFGHIFD",
xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary);
var body = "--" + boundary + "\r\n";
body += 'Content-Disposition: form-data; name="' + nameVar +'"; filename="' + fileName + '"\r\n';
body += "Content-Type: " + ctype + "\r\n\r\n";
//body += fileData;
end = "\r\n--" + boundary + "--";
//var body = fileData;
xhr.send(body + fileData + end);
return true;
}
When I feed payload into the function like fileUpload(url,payload,fileName,nameVar,ctype);, the part that should be binary data just transfers as [object Blob]. If I take out the body and only send the fileData,
var body = fileData;
xhr.send(body);
It transfers the binary data in the packet.
Why is the first function not sending the binary through?

Related

Is there any way to invoke a "print" to an HTML embed in Edge?

I have a base64 text of a pdf file, that I need to open the print dialog for.
So what I did for now is to create a new window, embedding that into the window for the user to manually click on the "print" at the embed toolbar.
var windowContent = '<!DOCTYPE html>';
windowContent += '<html>'
windowContent += '<head><title>Print</title></head>';
windowContent += '<body style=\"margin:0; padding:0; height:99.5vh\">'
windowContent += '<embed width=100% height=100% src="data:application/pdf;base64,' + dataUrl + '">';
windowContent += '</body>';
windowContent += '</html>';
var printWin = window.open('','','width=1000,height=1000');
printWin.document.open();
printWin.document.write(windowContent);
printWin.document.close();
printWin.focus();
//printWin.print();
The commented out print is because the embed loads the pdf async and it tries to print a blank page instead.
I cannot use external JS Libraries like "pdf.js" due to internal security reasons.
What I thought is, that if the embed has that "print" button, if it would be possible to trigger that by code?
Is this even possible?
Edited code due to Comment of Jan Pfeifer:
This still does not work properly. The Page is being loaded, but the print is not executing.
function myPrint(win) {
win.print();
}
var windowContent = '<!DOCTYPE html>' +
'<html>' +
'<head><title>Print MeasurementJob</title></head>' +
'<body style="margin:0; padding:0; height:99.5vh">' +
'<embed width="100%" height="100%" src="data:application/pdf;base64,' + dataUrl + '">' +
'</body>' +
'</html>';
var printWin = window.open('', '', 'width=1000,height=1000');
printWin.document.write(windowContent);
printWin.document.close();
printWin.focus();
var embed = printWin.document.querySelector('embed');
embed.addEventListener('load', function() { myPrint(printWin); });
The only solution that seems to work now is to convert Base64 PDF to the BLOB and open it in new window:
Creating a BLOB from a Base64 string in JavaScript
const b64toBlob = (b64Data, contentType = '', sliceSize = 512) => {
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize),
byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
const blob = new Blob(byteArrays, { type: contentType });
return blob;
}
Open a new window and call print
const base64PDF = ""; // Your Base64 PDF data
const contentType = "application/pdf";
const blob = b64toBlob(base64PDF, contentType);
const blobUrl = URL.createObjectURL(blob);
const w = window.open(blobUrl, '', 'width=1000,height=1000');
w.print();

Laravel Downloaded file from the browser gets corrupted

I am trying to download the file saved in s3. So I have codes like below.
static public function download($key, $disk = 's3') : object
{
$file = FileHelper::get($key);
$local = Storage::disk('local');
$local->put(basename($key), $file);
$pathToFile = storage_path().'/app/'.basename($key);
$headers = [
'Content-Type' => \GuzzleHttp\Psr7\mimetype_from_filename(basename($key)),
'Content-Description' => 'File Transfer',
'Content-Disposition' => "attachment; filename=' ". basename($key),
'filename'=> basename($key)
];
$repsonse = response()->download($pathToFile, basename($key), $headers)->deleteFileAfterSend(true);
ob_end_clean();
return $repsonse;
}
And I have the javascript code like below.
downloadFile() {
var app = this;
var url = baseRoute + 'files/' + this.fileId + '/download';
axios.get(url, {
headers: {
Authorization: 'bearer ' + this.Authentication.tokenData().token,
responseType : "blob"
}
}).then(function (response) {
var today = ((new Date()).toISOString()).slice(2,10),
filename = today.replace(/-/gi, "")+' '+ app.fileTitle;
let blob = new Blob([response.data], {
type: response.headers['content-type']
});
let link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
link.click();
}).catch(function (response) {
console.log(response);
});
}
The file is downloaded, however, the file is corrupted. (it would not open) I checked S3 bucket to see if the file is corrupted during upload, but it was fine there.
What am I doing wrong? Any advice or suggestion would be appreciated.
Thank you in advance.
Added: when I tried to view the image, it returned white squared which means its base64 encoded image. Does it have to do with being empty file?
I had to use standard XMLHttpRequest to work it like below.
downloadFile() {
var app = this;
var url = baseRoute + 'files/' + this.fileId + '/download';
var xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.setRequestHeader('Authorization', 'bearer ' + this.Authentication.tokenData().token);
xhr.responseType = "arraybuffer";
xhr.onload = function () {
if (this.status === 200) {
var today = ((new Date()).toISOString()).slice(2,10),
filename = today.replace(/-/gi, "")+' '+ app.fileTitle;
var blob = new Blob([xhr.response], {type: xhr.getResponseHeader('content-type')});
var objectUrl = URL.createObjectURL(blob);
let link = document.createElement('a');
link.href = objectUrl;
link.download = filename;
link.click();
window.URL.revokeObjectURL(blob);
}
};
xhr.send();
},

Facebook Javascript Form Data Photo Upload: requires upload file error

I've searched nearly every thread for 15-some hours and haven't found an answer to my problem. I'm using strictly Javascript to upload a photo to Facebook using form data. I decoded the base64 data from my JPEG image (originally svg -> canvas element), using this decode method (https://github.com/danguer/blog-examples/blob/master/js/base64-binary.js) and then converting it to a string in the form data format like https://gist.github.com/andyburke/1498758 (not using XMLHttpRequest). I then put that string as the "source" parameter in the post. I keep getting a "(#324) Requires upload file". It works perfectly if I just put in a url for the image. Can anybody help? Thanks!
// The function to post the image:
FB.api("/me/photos",
"POST",
{
"source": window.image_source,
"message": "...",
"fileName": "..."
},
function(response) {
console.log(response.error);
}
});
// How I got the image_source:
var img = canvas.toDataURL("image/jpeg");
var encoded = img.substring(img.indexOf(',')+1,img.length);
var decoded = decode(encoded);
convertToFormData(decoded);
function convertToFormData(imageData) {
var boundary = 'myboundary';
var formData = "Content-Type: multipart/form-data; boundary=" + boundary + '\r\n\r\n';
formData += '--' + boundary + '\r\n';
formData += 'Content-Disposition: form-data; name="source"; filename="' + "myfile" + '"\r\n';
for ( var i = 0; i < imageData.length; ++i )
formData += String.fromCharCode(imageData[i] & 0xff);
formData += '\r\n';
formData += '--' + boundary + '\r\n'+ 'Content-Disposition: form-data; name="message"\r\n\r\n';
formData += "Description" + '\r\n';
formData += '--' + boundary + '--\r\n';
window.image_source = formData;
}
function decode(input) {
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var lkey1 = keyStr.indexOf(input.charAt(input.length-1));
var lkey2 = keyStr.indexOf(input.charAt(input.length-2));
var bytes = (input.length/4) * 3;
if (lkey1 == 64) bytes--;
if (lkey2 == 64) bytes--;
var chr1, chr2, chr3; var enc1, enc2, enc3, enc4;
var i = 0;var j = 0;
var uarray = new Uint8Array(bytes);
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
for (i=0; i<bytes; i+=3) {
enc1 = keyStr.indexOf(input.charAt(j++));
enc2 = keyStr.indexOf(input.charAt(j++));
enc3 = keyStr.indexOf(input.charAt(j++));
enc4 = keyStr.indexOf(input.charAt(j++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
uarray[i] = chr1;
if (enc3 != 64) uarray[i+1] = chr2;
if (enc4 != 64) uarray[i+2] = chr3;
}
return uarray;
}
You have to turn the Canvas Image into a Blob to upload it to Facebook:
const dataURItoBlob = (dataURI) => {
let byteString = atob(dataURI.split(',')[1]);
let ab = new ArrayBuffer(byteString.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {
type: 'image/jpeg'
});
}
After that, you can use the blog with FormData:
formData.append('source', blob);
Source: http://www.devils-heaven.com/facebook-javascript-sdk-photo-upload-from-canvas/

Upload Base64 Image Facebook Graph API

I'm trying to upload a base64 image to a FaceBook page using Node.js. I have managed to get the upload working with all the multipart data etc should I read the file from the filesystem (ie. using fs.readFileSync('c:\a.jpg')
However, should I use the base64 encoded image and try upload it, it give me the following error : {"error":{"message":"(#1) An unknown error occurred","type":"OAuthException","code":1}}
I have tried converting it to binary by new Buffer(b64string, 'base64'); and uploading that, but no luck.
I have been struggling with this for 3 days now, so anyhelp would be greatly appreciated.
Edit : If anyone also knows how I could convert the base64 to binary and successfully upload it, that would also work for me.
Edit : Code Snippet
var postDetails = separator + newlineConstant + 'Content-Disposition: form-data;name="access_token"' + newlineConstant + newlineConstant + accessToken + newlineConstant + separator;
postDetails = postDetails + newlineConstant + 'Content-Disposition: form-data; name="message"' + newlineConstant + newlineConstant + message + newlineConstant;
//Add the Image information
var fileDetailsString = '';
var index = 0;
var multipartBody = new Buffer(0);
images.forEach(function (currentImage) {
fileDetailsString = fileDetailsString + separator + newlineConstant + 'Content-Disposition: file; name="source"; filename="Image' + index + '"' + newlineConstant + 'Content-Type: image/jpeg' + newlineConstant + newlineConstant;
index++;
multipartBody = Buffer.concat([multipartBody, new Buffer(fileDetailsString), currentImage]); //This is what I would use if Bianry data was passed in
currentImage = new Buffer (currentImage.toString('base64'), 'base64'); // The following lines are what I would use for base64 image being passed in (The appropriate lines would be enabled/disabled if I was using Binary/base64)
multipartBody = Buffer.concat([multipartBody, new Buffer(fileDetailsString), currentImage]);
});
multipartBody = Buffer.concat([new Buffer(postDetails), multipartBody, new Buffer(footer)]);
I hope this will be useful. By doing photo upload to FB only with the help of javascript you can use the following method. Required thing here are imageData(which is base64 format of image) and the mime type.
try {
blob = dataURItoBlob(imageData,mimeType);
} catch (e) {
console.log(e);
}
var fd = new FormData();
fd.append("access_token",accessToken);
fd.append("source", blob);
fd.append("message","Kiss");
try {
$.ajax({
url:"https://graph.facebook.com/" + <<userID received on getting user details>> + "/photos?access_token=" + <<user accessToken>>,
type:"POST",
data:fd,
processData:false,
contentType:false,
cache:false,
success:function(data){
console.log("success " + data);
},
error:function(shr,status,data){
console.log("error " + data + " Status " + shr.status);
},
complete:function(){
console.log("Ajax Complete");
}
});
} catch(e) {
console.log(e);
}
function dataURItoBlob(dataURI,mime) {
// convert base64 to raw binary data held in a string
// doesn't handle URLEncoded DataURIs
var byteString = window.atob(dataURI);
// separate out the mime component
// write the bytes of the string to an ArrayBuffer
//var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
// write the ArrayBuffer to a blob, and you're done
var blob = new Blob([ia], { type: mime });
return blob;
}
//EDIT AJAX SYNTAX
The code above didn't quite work for me (Missing comma after type:"POST", and data URI to blob function reported errors. I got the following code to work in Firefox and Chrome:
function PostImageToFacebook(authToken)
{
var canvas = document.getElementById("c");
var imageData = canvas.toDataURL("image/png");
try {
blob = dataURItoBlob(imageData);
}
catch(e) {
console.log(e);
}
var fd = new FormData();
fd.append("access_token",authToken);
fd.append("source", blob);
fd.append("message","Photo Text");
try {
$.ajax({
url:"https://graph.facebook.com/me/photos?access_token=" + authToken,
type:"POST",
data:fd,
processData:false,
contentType:false,
cache:false,
success:function(data){
console.log("success " + data);
},
error:function(shr,status,data){
console.log("error " + data + " Status " + shr.status);
},
complete:function(){
console.log("Posted to facebook");
}
});
}
catch(e) {
console.log(e);
}
}
function dataURItoBlob(dataURI) {
var byteString = atob(dataURI.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], { type: 'image/png' });
}
Here's the code at GitHub
https://github.com/DanBrown180/html5-canvas-post-to-facebook-base64
Dan's Answer works the best. Something else that might be helpful in this scenario is the optional argument for posting photos: 'no_story'. This arg defaults to true forcing the photo-post to skip the user's wall. By adding
fd.append("no_story", false);
you can update the user's wall with the photo-post.
I would have just left this as a comment but... 50 Rep for comments.
We can simplify image recoding by usage of the modern Fetch API instead of Uint8Array.
var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
fetch(url)
.then(res => res.blob())
.then(blob => console.log(blob))`
I did something very similar to your question. I had a webcam-snapshot that needed to be POSTed to a Facebook Fan Page.
The setup was in a restaurant where people could take a picture and it would be posted onto the Restaurants Page. People would then see a QR code to the posted facebook-photo which they could choose to share on their own profile.
Hope this can help somebody because I searched a lot to get to this working SOLUTION
Note: My image is BASE64 encoded already.
//imageData is a base64 encoded JPG
function postSocial(imageData, message){
var ia = toUInt8Array(imageData);
postImageToFacebook(mAccessTokenPage, "imageName", "image/jpeg",ia, message);
}
function toUInt8Array(dataURI) {
// convert base64 to raw binary data held in a string
// doesn't handle URLEncoded DataURIs
var byteString = window.atob(dataURI);
// write the bytes of the string to an ArrayBuffer
//var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return ia;
}
function postImageToFacebook( authToken, filename, mimeType, imageData, message ) {
// this is the multipart/form-data boundary we'll use
var boundary = '----ThisIsTheBoundary1234567890';
// let's encode our image file, which is contained in the var
var formData = '--' + boundary + '\r\n'
formData += 'Content-Disposition: form-data; name="source"; filename="' + filename + '"\r\n';
formData += 'Content-Type: ' + mimeType + '\r\n\r\n';
for ( var i = 0; i < imageData.length; ++i )
{
formData += String.fromCharCode( imageData[ i ] & 0xff );
}
formData += '\r\n';
formData += '--' + boundary + '\r\n';
formData += 'Content-Disposition: form-data; name="message"\r\n\r\n';
formData += message + '\r\n'
formData += '--' + boundary + '--\r\n';
var xhr = new XMLHttpRequest();
xhr.open( 'POST', https://graph.facebook.com/ + {PAGE_ID} + "/photos?access_token=" + authToken, true );
xhr.onload = function() {
// ... Fill in your own
//Image was posted
console.log(xhr.responseText);
};
xhr.onerror = function(){
console.log("Error while sending the image to Facebook");
};
xhr.setRequestHeader( "Content-Type", "multipart/form-data; boundary=" + boundary );
xhr.sendAsBinary( formData );
}
Here is how I was able to post an image to facebook using the facebook JS API.
I am using the canvas HTML5 functionality. It's not fully supported by every browser.
You need first to get the image data. Then to encapsulate it in a form data.
I then use the FB.login API in order to retrieve the access token and the userID.
var data = $('#map >> canvas').toDataURL('image/png');
var blob;
try {
var byteString = atob(data.split(',')[1]);
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
blob = new Blob([ab], {type: 'image/png'});
} catch (e) {
console.log(e);
}
var fd = new FormData();
fd.append("source", blob);
fd.append("message", "Photo Text");
FB.login(function(){
var auth = FB.getAuthResponse();
$.ajax({
url:"https://graph.facebook.com/"+auth.userID+"/photos?access_token=" + auth.accessToken,
type:"POST",
data:fd,
processData:false,
contentType:false,
cache:false,
success:function(data){
console.log("success " + data);
},
error:function(shr,status,data){
console.log("error " + data + " Status " + shr.status);
},
complete:function(){
console.log("Ajax Complete");
}
});
}, {scope: 'publish_actions'});
Here is an example that does not need jQuery or other libraries, just the native Fetch API:
const dataURItoBlob = (dataURI) => {
let byteString = atob(dataURI.split(',')[1]);
let ab = new ArrayBuffer(byteString.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {
type: 'image/jpeg'
});
}
const upload = async (response) => {
let canvas = document.getElementById('canvas');
let dataURL = canvas.toDataURL('image/jpeg', 1.0);
let blob = dataURItoBlob(dataURL);
let formData = new FormData();
formData.append('access_token', response.authResponse.accessToken);
formData.append('source', blob);
let responseFB = await fetch(`https://graph.facebook.com/me/photos`, {
body: formData,
method: 'post'
});
responseFB = await responseFB.json();
console.log(responseFB);
};
document.getElementById('upload').addEventListener('click', () => {
FB.login((response) => {
//TODO check if user is logged in and authorized publish_actions
upload(response);
}, {scope: 'publish_actions'})
})
Source: http://www.devils-heaven.com/facebook-javascript-sdk-photo-upload-from-canvas/

upload to php $_FILE from chrome extension

I'm trying to upload an image via POST with javascript to a site that I can't modify the source of.
The page has a form that allows you to upload images:
<form enctype="multipart/form-data" action="/u.php" method="post">
<input name="file" type="file"> <input type="submit" value="Upload File">
</form>
I want to be able to upload images with javascript but I can't get anything to work, I'm not sure if this is even possible...
My JS so far:
file = document.getElementById('fileinput').files[0];
r = new FileReader();
r.onloadend = doUpload;
r.readAsBinaryString(file)
function doUpload(el){
file = el.target.result;
XMLHttpRequest.prototype.sendAsBinary = function(string) {
var bytes = Array.prototype.map.call(string, function(c) {
return c.charCodeAt(0) & 0xff;
});
this.send(new Uint8Array(bytes).buffer);
};
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://upload.domain.com/u.php', true);
var boundary = 'ohaiimaboundary';
xhr.setRequestHeader(
'Content-Type', 'multipart/form-data; boundary=' + boundary);
xhr.sendAsBinary([
'--' + boundary,
'Content-Disposition: form-data; name="file"; filename="test.jpg"',
'Content-Type: multipart/form-data',
'',
file,
'--' + boundary + '--'
].join('\r\n'));
}
Thanks
EDIT:
figured this one out, kind of, this should work with a little modification (png is hardcoded in)
function doUpload(fl){
var file = fl.target.result;
XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
var bb = new BlobBuilder();
var data = new ArrayBuffer(1);
var ui8a = new Uint8Array(data, 0);
for (var i in datastr) {
if (datastr.hasOwnProperty(i)) {
var chr = datastr[i];
var charcode = chr.charCodeAt(0)
var lowbyte = (charcode & 0xff)
ui8a[0] = lowbyte;
bb.append(data);
}
}
var blob = bb.getBlob();
this.send(blob);
}
var xh = new XMLHttpRequest();
xh.open('post', 'http://upload.domain.com/u.php', true);
xh.onreadystatechange = function(){
if(this.readyState != 4){
return;
}
else{
console.log(this.responseText);
}
};
var boundary = '--fgsfds--';
xh.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
xh.sendAsBinary([
'--' + boundary,
'Content-Type: image/png',
'Content-Disposition: form-data; name="file"; filename="testz.png"',
'',
file,
'--' + boundary + '--',
''].join('\n'));
}
function mkUpload(){
var r = new FileReader();
r.onloadend = doUpload;
r.readAsBinaryString(document.upload.file.files[0]);
}
test PHP:
&lt?
echo sprintf('<pre>%s</pre>', print_r($_FILES, true));
?>
Actually, have you tried xhr.send(FormData)? FormData allows you to
append File objects, which will be treated as binary content and using
it with XHR sends a multipart/form-data. There's no need to construct it yourself.
var formData = new FormData();
var file = document.querySelector('input[type="file"]').files[0];
if (file) {
formData.append("file", file);
}
var xhr = new XMLHttpRequest();
xhr.open('POST', '/u.php', true);
xhr.onload = function(e) { ... };
xhr.send(formData);

Categories

Resources