How to send blob appended in formData to php - javascript

Issue : While uploading large image files i recognized that while uploading on my AWS server having 1gb memory uses it's full capacity, it goes upto 932 mb usage which causes crash to the process. I was saving that image in the form of DataURI and then I read somewhere that saving it in the form of blob can solve my problem. So i want to append that blob to formData and send to server and this is the reason i come up with this question. However if any else suggestion regarding the same problem to save image more efficient way when memory is concerned, will be appreciated.
Motive
I want to send an image to the server side as in the form of a blob.
What I have done
I am currently having a dataURI which I have converted into a blob. Further, i append that blob to formData and try to send it to server side/php using ajax.
JAVASCRIPT:
function convertURIToImageData(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
//
const dataURIconverter = () =>{
let img;
var image = new Image();
image.crossOrigin = 'anonymous'; // cross domain
// create an empty canvas element
var canvas = document.createElement("canvas"),
canvasContext = canvas.getContext("2d");
image.onload = function () {
//Set canvas size is same as the picture
canvas.width = image.width;
canvas.height = image.height;
// draw image into canvas element
canvasContext.drawImage(image, 0, 0, image.width, image.height);
// get canvas contents as a data URL (returns png format by default)
var dataURL = canvas.toDataURL();
// console.log(dataURL)
let blob = convertURIToImageData(dataURL)
console.log(blob)
var formData = new FormData();
formData.append('blobImage',blob)
$.ajax({
type: 'POST',
url: 'check.php',
data: formData,
processData: false
}).done(function(data) {
console.log(data);
})
}
image.src = "https://static.pexels.com/photos/248797/pexels-photo-248797.jpeg"
}
dataURIconverter()
PHP
<?php
var_dump($_POST['blobImage'])
var_dump($_POST);
//var_dump($_FILES['image']);
//$name = $_FILES['image']['tmp_name'];
//echo $name;
//echo $_FILES['image']['tmp_name'];
//$status = move_uploaded_file($name, $_FILES['image']['name']);
//echo 'successfully stored at '.$_SERVER['HTTP_HOST'];
?>
Error
I am receiving null as in console and i also checked the headers where i see formData with the name
As you can see, $_POST showing the blob but $_POST['blobImage'] is showing null.
Solution I require:
i am not that quick to php so i am not sure if i am sending the blob in the right way or receiving it.
I have provided my all possible efforts i have taken to achieve my motive.
Thanks to the community for help.

Add the following three properties on your jQuery Ajax call , they are required for blobs :
cache: false,
contentType: false,
processData: false
Then do not use formData in the data property of your Ajax Call , you simply need to add your created blob.
Also add a small rendering callback (apart from the console.log you already use) to print the Image. Your AJAX call gets like this :
$.ajax({
type: 'POST',
url: 'check.php',
data: blob,
cache: false,
contentType: false,
processData: false
}).done(function(data) {
document.write("<img src='"+data+"'></img>");
})
Change your PHP code to the following :
<?php
$res = file_get_contents("php://input");
echo "data:image/jpg;base64,".base64_encode($res);
?>
As far as the "php://input" use is concerned. It returns all the raw data that come after the headers of your request and it does not care what type they are which is pretty handy in most cases. Whereas $_POST will only wrap the data that have been passed with the following Content-Types :
application/x-www-form-urlencoded
multipart/form-data
If you really want to use FormData then you can change the request to the following :
$.ajax({
type: 'POST',
url: 'check.php',
data: formData,
cache: false,
contentType: false,
processData: false
}).done(function(data) {
console.log(data);
})
And you should also change your PHP file to get the $_FILE. Sending data this way , the Content-Type of the Request will be "multipart/form-data" which will have blobs , images and generally files on the $_FILES and the rest on the $_POST so the "php://input" will not be helpful.
<?php
var_dump($_FILES);
?>
Also keep in mind that when uploading blobs this way , they will get a random name , if you are not going to be generating filenames on the Server-Side (which you probably should in most cases) and want a specific name designated by the uploader , then you can pass it along with the FormData like :
formData.append('blobImage',blob, "MyBloBName");

If you set contentType: false in your jQuery Ajax call , you can still use the same code with formData and then access the file on the server through $_FILES['blobImage']

The problem is that $_REQUEST, and therefore $_GET and $_POST objects have a limitation to the number of characters available to them.
post_max_size
in PHP.ini controls the maximum size of post.
Browsers and their implementations of $_GET control the limit of a $_GET request. As it appears in their URL bar. For example IE9's limit is 2000 characters so if your blob ends up as anything more than 2000 characters in the $_GET Request. The general consensus is that $_GET requests should be much less than 255 bytes. And if you approach this limit be careful because older browsers and protocols are completely unprepared for this.

Related

Creating a file from a blob

I'm in need of some javascript guru. I have this code:
handleImage(new File([blob], blob.name, {type: blob.type})).done(/* something */)
and
handleImage = function (image) {
// create some fake form data
var formData = new FormData();
formData.append("attachment", image);
formData.append("auto", true);
formData.append("_csrf", "xxxxxxxxx");
// post to the server.
return $.ajax({
url: "/some/url",
data: formData,
cache: false,
contentType: false,
processData: false,
type: 'POST',
error: function () {
console.log("error");
}
});
This works fine with Chrome and Firefox, but when using Safari (10.1.1), the server (java / spring mvc) receive in the MultipartHttpServletRequest an empty file for "attachment". So it seems to me that new File([blob], blob.name, {type: blob.type}) is somehow failing.
Any idea of what's wrong here?
This is probably a bug in safari's young implementation.
But why do you even convert it to a File object ?
A File object is a Blob, the only difference being that it has a name and a lastModified properties. But since you already seem to extend your blob, it leaves only this lastModifiedproperty that you could add too anyway.
The only API I can think of, where it makes a difference if your object is a Blob or a File is FormData.append method ; where if you pass a File object, it will be able to set the filename automatically. But this method has a third parameter, allowing you to set this filename.
So if you change your code to include formData.append("attachment", image, image.name); and call it with handleImage(blob) directly, it will do exactly the same request as the one you're doing, except that it will work on Safari and every other browser that don't support the File constructor (looking at you IE).

Javascript: converting binary data in Uint8Array to string corrupting file

I'm trying to upload a video file in chunks to a server via a standard jQuery $.ajax call. The process is simple:
Use the slice() method on the file object to read a chunk
Use FileReader.readAsArrayBuffer to read the resulting blob
Create a Uint8Array of that result in the FileReader.onload callback
Use String.fromCharCode.apply(null, intArrayName) to convert it into a binary string
Upload that binary string as a chunk in the AJAX call's data property
When I finish uploading all the chunks, the server's API complains that the file upload is incomplete. To test this method, I converted the concatenated binary strings into a Blob and had Chrome save it to my downloads folder, only to find out my media player said the file was unplayable.
I've read posts on this site and other articles suggesting that directly converting binary data represented by integers to strings results in a loss of data, and that the suggestion is convert it to a base64-encoded string, since Javascript doesn't have a StreamContent object like C# does.
The problem is that even if I set Content-Transfer-Encoding to base64 I don't think the API (which wasn't written by us) picks up on it and can tell that a base64 decoding is needed.
My question is: is this the only sure-fire way of sending video data over an AJAX call safely? If not, how else can I send it in the data field of my AJAX request? There are probably thousands of uploaders like this on the Internet but nowhere can I find solid documentation on how to do this. If it turns out that the server needs to expect base64 encoding, we might have to write a middle-man API to do this decoding for us.
Thanks to Patrick Evans, it turns out all I needed to do was upload the blob itself:
function upload(file) {
var offset = 0; // what's been sent already
var fileSize = file.size;
var chunkSize = 64 * 1024; // bytes
while (offset < fileSize) {
var blob = file.slice(offset, chunkSize + offset);
var urlToUse = ((offset + blob.size) >= fileSize) ? videoFinishBaseUrl : videoContinueBaseUrl;
$.ajax({
url: urlToUse,
method: 'POST',
data: blob,
headers: requestHeaders,
contentType: false,
processData: false,
cache: false,
async: false,
success: function(data) {
jQuery('#uploadmsg').text('Finished offset ' + offset);
offset += blob.size;
},
error: function(err) {
jQuery('#uploadmsg').text(err);
}
});
}
};

How to create binary blob from atob - currently getting different bytes

I've got a binary Excel file created on the server that I'm returning from a C# WebMethod using Convert.ToBase64String(FileData) called from a JavaScript/JQuery $ajax call. I've confirmed the base64 string data gets to the client, but when I attempt to convert it to a binary blob and save it, the bytes saved to disk aren't the same as are on the server. (I'm getting lots of 0xC3 etc. bytes, which look suspiciously like utf8 double byte injections)
$.ajax({
type: "POST",
contentType: "application/json;",
dataType: "json",
processData: false,
data: "{ inputData: \"" + dataString + "\" }",
url: "Api.aspx/GetExcel",
success: ...
success handler code includes:
var excelBlob = new Blob([atob(msg.d)], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;' });
...
var a = document.createElement('a');
...
a.href = window.URL.createObjectURL(excelBlob);
a.setAttribute('download', 'Excel.xlsx');
When it completes download it has bad bytes values. Binary comparison with source shows it's close but has C3 and similar values inserted or munged into place.
Is there something I'm doing wrong or missing to get my Base64 string correctly converted to a client binary blob?
The new Blob constructor encodes any strings it encounters as UTF-8 (http://dev.w3.org/2006/webapi/FileAPI/#constructorBlob). Since you are dealing with binary data this get's converted into UTF-8 multi-byte representations.
Instead you need to convert your data into an array of bytes before passing to the Blob constructor.
The following code works for me in Chrome:
var binary = atob(base64)
var array = new Uint8Array(binary.length)
for( var i = 0; i < binary.length; i++ ) { array[i] = binary.charCodeAt(i) }
new Blob([array])
That said I don't know if atob is well defined across browsers (I'm guessing there's a reason mozilla provides much longer example code https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#Solution_.232_.E2.80.93_rewriting_atob%28%29_and_btoa%28%29_using_TypedArrays_and_UTF-8).

Javascript Blob changes file

I really have no prior knowledge to Blob objects in JavaScript aside from what I've read here but need to use them to convert a string of binary data into a .xls file which I then make available to the user. The catch is when I construct a blob, get the location, and open up the file to look at it, it opens up saying
The file you are trying to open is in a different format that
specified by the file extension. Verify that the file is not
corrupted and is from a trusted source before opening the file.
(I know this data is incorrect because when I submit the form normally I get the file correctly and don't have this issue)
This is done in an ajax call and the success functions parameter data is the binary data.
$("#fileForm").submit(function(){
var fileData = $("#fileInputElmt").prop("files")[0];
var data = new FormData();
data.append("upload",fileData);
var url = "process.action?" + $("#fileForm").serialize();
$.ajax({
type: "POST",
url:url,
data:data,
cache:false,
contentType:false,
processData:false,
success:function(data){
var bb = new Blob([data],
{ type: 'application/vnd.ms-excel',endings:'native'});
var bUrl = URL.createObjectURL(bb);
window.open(bUrl,"_self");
hideProgressBar();
},error:function(data){
hideProgressBar();
}
});
return false;
});
Am I doing something wrong? or is there a better way of doing this?

Save chart image with open flash chart2

I am using Open Flash Chart 2 to create some graphs. I want to be able to save an image of the graph, which OFC2 supplies some methods to accomplish this. I used the example on the OFC2 site to directly display the raw image data on the page, but that does not work in IE6, which most of our users are using (I know, I know).
I switched to using the OFC2 method, post_image to post the raw image data to the server. I use a Perl script to receive the image data, save it to a file, and I can view the image. The unfortunate part about using the post_image method is that ActionScript throws an error when saving the image:
Error #2101: The String passed to URLVariables.decode() must be a URL-encoded query string containing name/value pairs.
Which apparently is a bug in Adobe - see this page. Because of this error, the post_image method does not complete successfully, so the javascript callback won't ever fire - I basically don't have a way to tell if the image was saved successfully.
So, I thought I would use the get_img_binary method of OFC2 to get the binary data of the image, and use jQuery to post the binary data to my Perl script.
I cannot figure out how to send the binary data correctly, or how to let my Perl script receive the binary data correctly, or both.
Here is my jQuery function:
var chartObj = $("#chart_object").get(0);
$.ajax({
type: "POST",
url: 'download_image.pl',
//contentType: 'application/octet-stream',
contentType: 'image/png',
//processData: false,
//data: { imgData: chartObj.get_img_binary() },
data: chartObj.get_img_binary(),
dataType: "text",
success: function(data) {
console.log( data );
}
});
You can see from some of my commented out lines that I have tried various contentTypes and other settings of the Ajax call.
The Ajax call is sending some data, but it doesn't appear to be binary. I think it is a base64 representation of the binary data.
Does anyone have any ideas on how to send binary data from javascript to the server?
The Perl script I have works fine for the post_image method, so I don't think the problem is there?
Thanks in advance!
I seem to have stumbled onto the solution.
Here is my ajax call:
var chartObj = $("#chart_object").get(0);
$.ajax({
type: "POST",
url: 'download_image.pl',
contentType: 'application/octet-stream',
processData: false,
data: imgData,
dataType: "text",
success: function(data) {
console.log( data );
}
});
And here is my Perl snippet to process/save the image:
use CGI qw(:standard);
use MIME::Base64;
...
my $img64 = param('POSTDATA');
my $img = decode_base64( $img64 );
...
#then print $img out to a file in binary mode
I had to decode the base64 representation of the PNG file, and then save it to a file.
i've got trouble too with using IE6 and OFC2 for saving image... So here are the scripts i use (javascript + PHP)
i know it's not very beautifull but jQuery doesn't want to work in a popup created via window.open('') on my IE6 so i decided to use a "old school method" to get it...
// javascript in the page displaying the flash chart
OFC = {};
OFC.jquery = {
name: "jQuery",
image: function(src) { return '<img src="data:image/png;base64,' + $('#'+src)[0].get_img_binary() + '" \/>'},
popup: function(src) {
var img_tag = OFC.jquery.image(src);
var img_win = window.open('', 'imagesave');
img_win.document.write('<html><head><title>imagesave<\/title><\/head><body>'+ img_tag + '<\/body><\/html>');
img_win.document.close();
},
popupie: function(src) {
var img_data = "image/png;base64,"+$("#"+src)[0].get_img_binary();
var img_win = window.open('', 'imagesave');
with(img_win.document) {
write('<html>');
write('<head>');
write('<title>imagesave<\/title>');
write('<\/head>');
write('<body onload="document.forms[0].submit()">');
write('<form action="\/ofc\/base64post.php" method="post">');
write('<input type="hidden" name="d" id="data" value="'+img_data+'" \/>');
write('<\/form>');
write('<div><img src="\/ofc\/ajax-loader.gif" border="0" alt="" \/><\/div>');
write('<div style="font-family: Verdana;">Please wait<br \/>After you can save the image<\/div>');
write('<\/body>');
write('<\/html>');
}
img_win.document.close();
}
}
function save_image() { // this function is automatically called
if ($.browser.msie)
OFC.jquery.popupie("my_chart"); // only for IE navigators
else
OFC.jquery.popup("my_chart"); // for the others
}
so, when we use the save_image() function (which is automaticaly called when you right clic dans select "Save Image Locally" on the flahs chart)
the image of the chart is tranfered to the popup and the data (base64 binary image) are posted to a php script /ofc/base64post.php that rander the picture :
<?php
// base64post.php
$data = split(';', $_POST['d']);
$type = $data[0];
$data64 = split(',', $data[1]);
$pic = base64_decode($data64[1]);
header("Content-type: $type");
echo $pic;
?>
hope that help someone !

Categories

Resources