jsPDF - send pdf to server ends up corrupted - javascript

I generate a pdf with text and images using javascript jsPDF library. Then I want to send the file to server in order to send an email with it. The problem is that the file that arrives on server is corrupted and can't be opened or I can't see the images on the pdf.
My code:
var pdf = btoa(doc.output());
- this gives an error: Uncaught InvalidCharacterError: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.
var pdf = btoa(encodeURIComponent(doc.output()));
var data = new FormData();
data.append("data" , pdf);
var xhr = new XMLHttpRequest();
xhr.open( 'post', '/url', true );
xhr.send(data);
I also tried other things like:
var pdf = btoa(encodeURIComponent(doc.output('blob'))); - the file can't be opened
var pdf = btoa(doc.output('blob')); - the file can't be opened
var pdf = btoa(unescape(encodeURIComponent(doc.output()))); - the file will open but the images are some grey lines
PS: I am using Laravel 5. Server code:
$data = base64_decode($_POST['data']);
$fname = "test.pdf";
$file = fopen("pdf/" .$fname, 'w');
fwrite($file, $data);
fclose($file);
SOLUTION:
js code:
var blob = doc.output('blob');
var fd = new FormData();
fd.append('data', blob);
$.ajax({
type: 'POST',
url: '/url',
data: fd,
processData: false,
contentType: false
}).done(function(data) {
console.log(data);
});
server code:
if(!empty($_FILES['data'])){
move_uploaded_file(
$_FILES['data']['tmp_name'],
public_path() . '/test.pdf'
);
return "Pdf was successfully saved.";
} else {
return "No Data Sent";
}

btoa is messed up with the ascii byte range... javascript can't hold all character. That's the reason for why you shouldn't use FileReader's readAsBinaryString either... Handle binary the right way, not as string or ~3x larger base64 string, but as a blob, ArrayBuffer or a typed array and it will turn out fine
var blob = doc.output('blob')
xhr.send(blob)

Related

Is it possible to upload a file to <input type="file"> without clicking the "choose file" button? [duplicate]

I have a blob data in this structure:
Blob {type: "audio/wav", size: 655404, slice: function}
size: 655404
type: "audio/wav"
__proto__: Blob
It's actually sound data recorded using the recent Chrome getUerMedia() and Recorder.js
How can I upload this blob to the server using jquery's post method? I've tried this without any luck:
$.post('http://localhost/upload.php', { fname: "test.wav", data: soundBlob },
function(responseText) {
console.log(responseText);
});
You can use the FormData API.
If you're using jquery.ajax, you need to set processData: false and contentType: false.
var fd = new FormData();
fd.append('fname', 'test.wav');
fd.append('data', soundBlob);
$.ajax({
type: 'POST',
url: '/upload.php',
data: fd,
processData: false,
contentType: false
}).done(function(data) {
console.log(data);
});
2019 Update
This updates the answers with the latest Fetch API and doesn't need jQuery.
Disclaimer: doesn't work on IE, Opera Mini and older browsers. See caniuse.
Basic Fetch
It could be as simple as:
fetch(`https://example.com/upload.php`, {method:"POST", body:blobData})
.then(response => console.log(response.text()))
Fetch with Error Handling
After adding error handling, it could look like:
fetch(`https://example.com/upload.php`, {method:"POST", body:blobData})
.then(response => {
if (response.ok) return response;
else throw Error(`Server returned ${response.status}: ${response.statusText}`)
})
.then(response => console.log(response.text()))
.catch(err => {
alert(err);
});
PHP Code
This is the server-side code in upload.php.
<?php
// gets entire POST body
$data = file_get_contents('php://input');
// write the data out to the file
$fp = fopen("path/to/file", "wb");
fwrite($fp, $data);
fclose($fp);
?>
You actually don't have to use FormData to send a Blob to the server from JavaScript (and a File is also a Blob).
jQuery example:
var file = $('#fileInput').get(0).files.item(0); // instance of File
$.ajax({
type: 'POST',
url: 'upload.php',
data: file,
contentType: 'application/my-binary-type', // set accordingly
processData: false
});
Vanilla JavaScript example:
var file = $('#fileInput').get(0).files.item(0); // instance of File
var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload.php', true);
xhr.onload = function(e) { ... };
xhr.send(file);
Granted, if you are replacing a traditional HTML multipart form with an "AJAX" implementation (that is, your back-end consumes multipart form data), you want to use the FormData object as described in another answer.
Source: New Tricks in XMLHttpRequest2 | HTML5 Rocks
I could not get the above example to work with blobs and I wanted to know what exactly is in upload.php. So here you go:
(tested only in Chrome 28.0.1500.95)
// javascript function that uploads a blob to upload.php
function uploadBlob(){
// create a blob here for testing
var blob = new Blob(["i am a blob"]);
//var blob = yourAudioBlobCapturedFromWebAudioAPI;// for example
var reader = new FileReader();
// this function is triggered once a call to readAsDataURL returns
reader.onload = function(event){
var fd = new FormData();
fd.append('fname', 'test.txt');
fd.append('data', event.target.result);
$.ajax({
type: 'POST',
url: 'upload.php',
data: fd,
processData: false,
contentType: false
}).done(function(data) {
// print the output from the upload.php script
console.log(data);
});
};
// trigger the read from the reader...
reader.readAsDataURL(blob);
}
The contents of upload.php:
<?
// pull the raw binary data from the POST array
$data = substr($_POST['data'], strpos($_POST['data'], ",") + 1);
// decode it
$decodedData = base64_decode($data);
// print out the raw data,
echo ($decodedData);
$filename = "test.txt";
// write the data out to the file
$fp = fopen($filename, 'wb');
fwrite($fp, $decodedData);
fclose($fp);
?>
I was able to get #yeeking example to work by not using FormData but using javascript object to transfer the blob. Works with a sound blob created using recorder.js. Tested in Chrome version 32.0.1700.107
function uploadAudio( blob ) {
var reader = new FileReader();
reader.onload = function(event){
var fd = {};
fd["fname"] = "test.wav";
fd["data"] = event.target.result;
$.ajax({
type: 'POST',
url: 'upload.php',
data: fd,
dataType: 'text'
}).done(function(data) {
console.log(data);
});
};
reader.readAsDataURL(blob);
}
Contents of upload.php
<?
// pull the raw binary data from the POST array
$data = substr($_POST['data'], strpos($_POST['data'], ",") + 1);
// decode it
$decodedData = base64_decode($data);
// print out the raw data,
$filename = $_POST['fname'];
echo $filename;
// write the data out to the file
$fp = fopen($filename, 'wb');
fwrite($fp, $decodedData);
fclose($fp);
?>
I tried all the solutions above and in addition, those in related answers as well. Solutions including but not limited to passing the blob manually to a HTMLInputElement's file property, calling all the readAs* methods on FileReader, using a File instance as second argument for a FormData.append call, trying to get the blob data as a string by getting the values at URL.createObjectURL(myBlob) which turned out nasty and crashed my machine.
Now, if you happen to attempt those or more and still find you're unable to upload your blob, it could mean the problem is server-side. In my case, my blob exceeded the http://www.php.net/manual/en/ini.core.php#ini.upload-max-filesize and post_max_size limit in PHP.INI so the file was leaving the front end form but getting rejected by the server. You could either increase this value directly in PHP.INI or via .htaccess

How to convert BLOB to image using python

I need to send an image from client to server using blob
I have converted a image to BLOB in jquery (client side) and sending the blob to python flask (server side) the problem is, I can't recreate image from BLOB. I have tried the following code in python but failed to get the image
Jquery code to convert image to blob:
function changeFile() {
var file = this.files[0];
var reader = new FileReader();
reader.addEventListener('load', readFile);
reader.readAsText(file);
}
function readFile(event) {
document.body.textContent = event.target.result;
console.log(event.target.result);
appendFileAndSubmit(event.target.result);
}
function appendFileAndSubmit(imageUrl){
var ImageURL = imageUrl
var data = {
'logo':ImageURL
}
$.ajax({
url: 'http://sdsdss/User/imgBlob',
method: 'POST',
dataType : 'json',
data: JSON.stringify(data),
contentType: 'application/json; charset=utf-8'
}).done(function(response){
var datas = JSON.parse(response);
console.log(datas);
});
}
document.getElementById("inp").addEventListener("change", changeFile);
Python Code: To recreate BLOB to image
function getImage(self):
reqData = json.loads(request.data)
Logo = reqData['logo']
png_recovered = base64.decodestring(Logo)
f = open("temp.png", "w")
f.write(png_recovered)
f.close()
Don't read the file as text, You are dealing with binary data. Best way to transfer binary with json is if you read the file as base64 instead reader.readAsDataURL(file) This will encode all bytes to a web safe base64 string (no slash or plus). Then you have to decode it with python as well.
I discourage you from using json when dealing with file transfer as it will increase the bandwidth with ~3 times as much (not to mention the time it also takes to decode and encode it back and forth) For this I recommend you instead use FormData.
var fd = new FormData()
fd.append('logo', files[0])
$.ajax({
url: 'http://sdsdss/User/imgBlob',
method: 'POST',
data: fd,
// Setting false prevent jQuery from transforming the data
processData: false,
contentType: false
}).then(function(response) {
console.log(JSON.parse(response))
})
or simpler yet, just post the file without any formdata, json or extra fields if they are not necessary.
fetch(uploudUrl, { method 'PUT', body: this.files[0] })

Sending URI data via AJAX POST

I got some trouble sending long text strings via html forms.
I want to send image data uri to a php page there can handle the data and save it in MySQL.
Image data example:
data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfO..
The data is coming from a clipboard paste function I have on the page.
I have no problem in saving the data on the php page, but getting the data to the page makes trouble.
The script below is the one i try use to send the data from the client page:
formData = new FormData();
formData.append('imagedata','data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfO..');
$.ajax({
url: "test.php?reportid=1",
type: "POST",
cache: false,
contentType: false,
processData: false,
data: formData
}).done(function(e){
alert(e);
});
It works fine if the image size is small, but if i got an image above 250KB, it loose data during the post.
Maybe someone has a better way to post the data to the server?
I found an alternative to transfer the data.
Using this solution for converting base64 code to blob
function dataURItoBlob(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
});
}
I change the code to this:
formData = new FormData();
var blob = dataURItoBlob('data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfO..');
formData.append('imagedata', blob, 'temp.png');
var request = new XMLHttpRequest();
request.open("POST", "test.php?reportid=1");
request.send(formData);
And then on the PHP page i receive data via $_FILES[];
$file = $_FILES['imagedata'];
$filetype = $file['type'];
$blob = file_get_contents($file['tmp_name']);
After that, can I insert it into the MySQL database.
Well, thanks anyway for the replies and suggestions :)
try JSON.parse('data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfO..');

turning canvas.toDataURL DOMString url into a blob,

i am using cropperjs to crop images
Using the method getCroppedCanvas to get reference to the cropped canvas image
Using the canvas method canvas.toDataURL to compress the images into a jpeg files.
I don't use canvas.toBlob as its browser support is not clear.
My question is how can i turn the string i get from canvas.toDataURL into a blob so i can later use it in formData.
Update
server expect form data - multi-part
One method that I used while working on some tool is to convert the dataURL to Blob using this function:
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
}
And then you can send the Blob to the server using native xhr like this:
var blob = dataURLtoBlob(dataURI);
var fd = new FormData();
var xhr = new XMLHttpRequest ();
fd.append ('file', blob);
xhr.open('POST', '/endpoint', true);
xhr.onerror = function () {
console.log('error');
}
xhr.onload = function () {
console.log('success')
}
xhr.send (fd);
The blob builder code is taken from here: https://stackoverflow.com/a/30407840/2386736
use canvas.toDataURL to generate an JPEG image:
var JPEG_QUALITY=0.5;
var dataUrl = canvas.toDataURL('image/jpeg', JPEG_QUALITY).replace('data:image/jpeg;base64,', '');
And then send it to AJAX as data-multi-part:
jQuery.ajax({
url: 'php/upload.php',
data: dataUrl,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
}
});
Depending on what your server side expects to receive.
By the way: You wrote "I don't use canvas.toBlob as its browser support is not clear" - You can see the browser support here:

How to send image file in inputstream format to server using XMLHttpRequest object

I have four parameters in webservice. The service is working gud i have tested using android application. But the same thing i cant do in phonegap.
Parameters : name,emailid,pass and imagefile. Image was in format of base64png. In server side i am receiving as Inputstream. Looking for help to send as in binary format.
body+= ServiceHttpHeader('name',name1);
body+= ServiceHttpHeader('emailid',emailid1);
body+= ServiceHttpHeader('pass',pass1);
body +='Content-Disposition: form-data; name=imagedetails;'
body += 'filename='+imagedetails+'\r\n';
body += "Content-Type: application/octet-stream\r\n\r\n ";
body +=imgdetailurl+'\r\n';
body += '--' + boundary + '--';
ImageUploadRequest.setRequestHeader('Cache-Control', 'no-cache');
ImageUploadRequest.send(body);
Phonegap has its own API to upload images to a server.
One solution can be that you upload the image to the server first. On successful upload, you pass all the other parameters, along with the path of the file on the server, to another script, to perform whatever processing you want to be done there.
Phonegap's documentation says that one should not use base64 encoded images for upload, because that often causes an error for the latest high res camera images. Use FILE_URI instead. You can read more here.
http://docs.phonegap.com/en/1.2.0/phonegap_file_file.md.html#FileTransfer
http://docs.phonegap.com/en/2.6.0/cordova_camera_camera.md.html#Camera (the documentation for camera.getPicture())
Example
function uploadPhotoToServer(imageURI)
{
var options = new FileUploadOptions();
options.fileKey="img_file";
options.fileName=imageURI.substr(imageURI.lastIndexOf('/')+1);
options.mimeType="image/jpeg";
window.localStorage.setItem("upload_file_name",options.fileName);
var params = new Object();
params.value1 = "test";
params.value2 = "param";
options.params = params;
options.chunkedMode = false;
var ft = new FileTransfer();
ft.upload(imageURI, encodeURI("<your image upload script url>"), onUploadSuccess, onFail, options);
}
function onUploadSuccess(r)
{
var filename = window.localStorage.getItem("upload_file_name");
file_path_on_server = "path/to/images/folder"+filename;
// other parameters
var email = <the email Id> ;
var name = <the name>;
var parameters = "name="+name+"&email="+email+"&img_path="+file_path_on_server;
$.ajax({
url: <your current script url>,
data: parameters ,
dataType: "jsonp",
timeout:10000,
success:function(json) {
alert(JSON.stringify(json, undefined,2));
},
error:function() {
alert("Error");
},
type:"GET"
});
function onFail()
{
alert("Error");
}
Hope this helps.

Categories