I am having a problem uploading a blob created in javascript to my server. The basic idea is that a user uploads an image and in javascript I center crop the image and downsample it before transmission.
The image manipulation is working fine, but the upload itself is not working right. Here is the code that does the upload and conversion from canvas to blob
function uploadCanvasData()
{
var canvas = $('#ImageDisplay').get(0);
var dataUrl = canvas.toDataURL("image/jpeg");
var blob = dataURItoBlob(dataUrl);
var formData = new FormData();
formData.append("file", formData);
var request = new XMLHttpRequest();
request.onload = completeRequest;
request.open("POST", "IdentifyFood");
request.send(formData);
}
function dataURItoBlob(dataURI)
{
var byteString = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++)
{
ia[i] = byteString.charCodeAt(i);
}
var bb = new Blob([ab], { "type": mimeString });
return bb;
}
The server claims that no files were uploaded, and when I use chrome to examine the request, I see the request payload as:
------WebKitFormBoundaryyzYbm61DKgS09tpB
Content-Disposition: form-data; name="file"
[object FormData]
------WebKitFormBoundaryyzYbm61DKgS09tpB--
In contrast to the payload of a form being submitted with input type="file"
------WebKitFormBoundaryUOn3WXb7pKLmOxRZ
Content-Disposition: form-data; name="imagefile"; filename="-3YQHiVaGWo.jpg"
Content-Type: image/jpeg
------WebKitFormBoundaryUOn3WXb7pKLmOxRZ--
So it looks to me like the XMLHttpRequest is just uploading the result of calling blob.toString()
Does anyone know what I am doing wrong here? Is there a better approach I should be using?
You have a typo in the function uploadCanvasData it should read
formData.append("file", blob);
Read your code more carefully!
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});
}
create an xmlhttpRequest
let uriPost ="active.php";
let xhrPost =new XMLHttpRequest();
then do this it easy
var dataURL = canvas.toDataURL('image/jpeg', 0.5);
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
I hope you'l do all this in a function that you will create your self then call that function
Related
Hi I have read but I have not found the answer so I ask:
How can I get the signature picture?
My Vuejs code is this one:
<VueSignaturePad width="100%" height="500px" ref="signaturePad" />
<div>
<button #click="save">Guardar</button>
<button #click="undo">Borrar</button>
</div>
My methods are:
undo() {
this.$refs.signaturePad.undoSignature();
},
save() {
this.loading = true;
e.preventDefault();
let currentObj = this;
const config = {
headers: { 'content-type': 'multipart/form-data' }
}
let formData = new FormData();
formData.append('signature', this.$refs.signaturePad.saveSignature());
axios.post('/api/signature/store?api_token='+App.apiToken, formData, config)
.then(function (response) {
currentObj.success = response.data.success;
})
}
My Laravel code has this:
$fileName = time().'_'.'signature'.'_'.$this->user->rut.'_'.date('d_m_Y').'.'.$request->file->getClientOriginalExtension();
$signature = new Signature;
$signature->rut = $this->user->rut;
$signature->signature = $fileName;
$signature->save();
Storage::disk('dropbox')->putFileAs(
'signatures/',
$request->file,
$fileName
);
The problem is that it displays me an error:
Call to a member function getClientOriginalExtension() on null
So I wonder how can I get the image?
this option worked for me to convert the base64 file to image before sending it
Vue code
<div id="app">
<vueSignature ref="signature" :sigOption="option" :w="'800px'" :h="'400px'" :disabled="disabled"></vueSignature>
<vueSignature ref="signature1" :sigOption="option"></vueSignature>
<button #click="save">Save</button>
<button #click="clear">Clear</button>
<button #click="handleDisabled">disabled</button>
</div>
functions
save(){
var _this = this;
var png = _this.$refs.signature.save()
var block = png.split(";");
// Get the content type of the image
var contentType = block[0].split(":")[1];// In this case "image/gif"
// get the real base64 content of the file
var realData = block[1].split(",")[1];// In this case "R0lGODlhPQBEAPeoAJosM...."
// Convert it to a blob to upload
var blob = this.b64toBlob(realData, contentType);
let data = new FormData()
data.append('img', blob)
axios.post(this.url, data).then(res=>{
console.log(res.data)
}).catch(function (error) {
console.log(error.response)
})
},
b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
},
I hope I've helped
I have added intervention/image package to convert image format in laravel.
image converted successfully but after uploading image quality was so bad.
Original Image
Uploaded Image
$img =(string) Image::make($image['base64'])
->resize(500, 500)->encode('jpg',100);;
$img = base64_encode($img);
To convert Heic image you have to use imagick, can you use this instead
This is how to install https://ourcodeworld.com/articles/read/645/how-to-install-imagick-for-php-7-in-ubuntu-16-04
try {
$image = new \Imagick();
$image->readImageBlob($image['base64']));
$image->setImageFormat("jpeg");
$image->setImageCompressionQuality(100);
$image->writeImage($targetdir.$uid.".jpg");
}
catch (\ImagickException $ex) {
/**#var \Exception $ex */
return new JSONResponse(["error" => "Imagick failed to convert the images, check if you fulfill all requirements." , "details" => $ex->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
}
A bit late, but I had the same problem.
I managed to do it with the heic2any js library (https://github.com/alexcorvi/heic2any/blob/master/docs/getting-started.md)
I converted the picture on client side, then gave it to the input in client side.
Server is seeing it as it was originally uploaded as jpg.
function convertHeicToJpg(input)
{
var fileName = $(input).val();
var fileNameExt = fileName.substr(fileName.lastIndexOf('.') + 1);
if(fileNameExt == "heic") {
var blob = $(input)[0].files[0]; //ev.target.files[0];
heic2any({
blob: blob,
toType: "image/jpg",
})
.then(function (resultBlob) {
var url = URL.createObjectURL(resultBlob);
$(input).parent().find(".upload-file").css("background-image", "url("+url+")"); //previewing the uploaded picture
//adding converted picture to the original <input type="file">
let fileInputElement = $(input)[0];
let container = new DataTransfer();
let file = new File([resultBlob], "heic"+".jpg",{type:"image/jpeg", lastModified:new Date().getTime()});
container.items.add(file);
fileInputElement.files = container.files;
console.log("added");
})
.catch(function (x) {
console.log(x.code);
console.log(x.message);
});
}
}
$("#input").change(function() {
convertHeicToJpg(this);
});
What I am doing is converting the heic picture to jpg, then previewing it.
After that I add it to the original input. Server side will consider it as an uploaded jpg.
Some delay can appear while converting, therefore I placed a loader gif while uploading.
The heic2any js library helped me accomplish this (https://github.com/alexcorvi/heic2any/blob/master/docs/getting-started.md)
On the client side, I converted the picture, then gave it to the server input. The server sees it as it was originally uploaded as PNG.
$('#files').on('change' , function(){
var total_file=document.getElementById("files").files.length;
for(var i=0;i<total_file;i++)
{
files = event.target.files[i];
var fileName = files.name;
var fileNameExt = fileName.substr(fileName.lastIndexOf('.') + 1);
objURL = URL.createObjectURL(event.target.files[i]);
if(fileNameExt == "heic") {
objURL = await convertHeicToJpg(input , i);
}
})
async function convertHeicToJpg(input , i)
{
var blobfile = $(input)[0].files[i]; //ev.target.files[0];
let blobURL = URL.createObjectURL(blobfile);
// convert "fetch" the new blob url
let blobRes = await fetch(blobURL)
// convert response to blob
let blob = await blobRes.blob()
// convert to PNG - response is blob
let resultBlob = await heic2any({ blob })
console.log(resultBlob)
var url = URL.createObjectURL(resultBlob);
let fileInputElement = $(input)[0];
let container = new DataTransfer();
let file = new File([resultBlob], "heic"+".png",{type:"image/png", lastModified:new Date().getTime()});
container.items.add(file);
fileInputElement.files[0] = container.files;
uploadFile(container.files);
console.log("added");
console.log(url);
return url ;
}
function uploadFile(files)
{
console.log(files);
var error = '';
var form_data = new FormData();
for(var count = 0; count<files.length; count++)
{
var name = files[count].name;
var extension = name.split('.').pop().toLowerCase();
form_data.append("files[]", files[count]);
}
$.ajax({
url:"<?php echo base_url(); ?>Property/upload",
method:"POST",
data:form_data,
contentType:false,
cache:false,
processData:false,
dataType:'JSON',
beforeSend:function(){
//..processing
},
success:function(data)
{
alert('image uploade')
}
})
}
In my page, I'm trying to display a thumbnails of my pdf using PDF JS and it works on local, but when I upload my code in my webserver the pdf file is auto download.
In my local :
Code :
$(function() {
var filePath = "http://example.com/public/uploads/docs/Document_One_1.pdf";
function Num(num) {
var num = num;
return function () {
return num;
}
};
function renderPDF(url, canvasContainer, options) {
var options = options || {
scale: 1.1
},
func,
pdfDoc,
def = $.Deferred(),
promise = $.Deferred().resolve().promise(),
width,
height,
makeRunner = function(func, args) {
return function() {
return func.call(null, args);
};
};
function renderPage(num) {
var def = $.Deferred(),
currPageNum = new Num(num);
pdfDoc.getPage(currPageNum()).then(function(page) {
var viewport = page.getViewport(options.scale);
var canvas = document.createElement("canvas");
canvas.setAttribute("id","pdfCanvas"+num);
canvas.setAttribute("onclick","popCanvas('{{url('/dashboard/showcanvas')}}','"+document.getElementById('pdfPath').innerHTML+"','"+num+"');");
var ctx = canvas.getContext('2d');
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
if(currPageNum() === 1) {
height = viewport.height;
width = viewport.width;
}
canvas.height = height;
canvas.width = width;
canvasContainer.appendChild(canvas);
page.render(renderContext).then(function() {
def.resolve();
});
})
return def.promise();
}
function renderPages(data) {
pdfDoc = data;
var pagesCount = pdfDoc.numPages;
for (var i = 1; i <= pagesCount; i++) {
func = renderPage;
promise = promise.then(makeRunner(func, i));
}
}
PDFJS.disableWorker = true;
PDFJS.getDocument(url).then(renderPages);
};
var body = document.getElementById("bodyofpdf");
renderPDF(filePath, body);
});
When I change the filepath url, for example: "https://www.tutorialspoint.com/bootstrap/bootstrap_tutorial.pdf", it works.
Can somebody help me ??
Sorry for my poor english.
If you are using Laravel you can control the behaviour of the routes, including the one to a .pdf. For local files, browsers have a different behaviour by default than for no local files.
I have used a lot PDF.js and I don't know why do you link the user directly to the file, you could create like a view page where it gets a parameter like the id or the name of the book and then you load it with pdf.js. If you use an url with a file extension you will probably confuse the browser.
yourpage.com/books/view/{id_book}
Bugs started when clients had migrating to FF 45. They tryed to submit forms with empty file inputs and it fail.
I detected that, if you submit form by AJAX with FormData, and it have empty file input - FF will not set Content-Type: application/octet-stream
I created minimal script to demonstrate this bug:
var fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.name = 'fileData';
var form = document.createElement('form');
form.method = 'post';
form.enctype = 'multipart/form-data';
form.appendChild(fileInput);
var xhr = new XMLHttpRequest();
xhr.open("POST", "/url");
xhr.send(new FormData(form));
I use standart FF45 on windows.
I didnt find any information about this bug, and use hack: delete empty file inputs before submited id:
var fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.name = 'fileData';
var form = document.createElement('form');
form.method = 'post';
form.enctype = 'multipart/form-data';
form.appendChild(fileInput);
// Filter empty file inputs
var childNodes = form.querySelectorAll('input[type=file]');
for (var i=0;i<childNodes.length;i++) {
if (childNodes[i].files.length === 0) {
childNodes[i].parentElement.removeChild(childNodes[i]);
}
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "/url");
xhr.send(new FormData(form));
//photo - image in Blob type
//no problems with it, checked with FileReader.readAsDataURL & <img>
var form = new FormData()
form.append('file1', photo, 'image.jpg')
ajax.post(url, form, callback) //no photos uploaded
Documentation of what I am trying to do: Uploading Files to the VK Server Procedure (step 2)
So, how should I pass my blob as POST parameter?
Image of the request
A complete File Upload exampe found at Mozilla Developer Network
https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications#Example.3A_Uploading_a_user-selected_file
You use FileReader.readAsBinaryString() to read the data and then XHR sendAsBinary() to push IO forward
function FileUpload(img, file) {
var reader = new FileReader();
this.ctrl = createThrobber(img);
var xhr = new XMLHttpRequest();
this.xhr = xhr;
var self = this;
this.xhr.upload.addEventListener("progress", function(e) {
if (e.lengthComputable) {
var percentage = Math.round((e.loaded * 100) / e.total);
self.ctrl.update(percentage);
}
}, false);
xhr.upload.addEventListener("load", function(e){
self.ctrl.update(100);
var canvas = self.ctrl.ctx.canvas;
canvas.parentNode.removeChild(canvas);
}, false);
xhr.open("POST", "http://demos.hacks.mozilla.org/paul/demos/resources/webservices/devnull.php");
xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
reader.onload = function(evt) {
xhr.sendAsBinary(evt.target.result);
};
reader.readAsBinaryString(file);
}