I have an image generated on the client side which I want to transfer to the server through a form. For example, let's say that I have a registration form in which the profile image is generated automatically by JavaScript, and I want to transfer that image to django.
What is the best way to transfer the image binary data to the server when user hit the submit button? what form field should I use?
thanks!
Found an answer myself, here's the solution in case someone needs it:
In the client side, this is how you get the image from canvas and set it to a form field (a hidden char field):
var dataURL = document.getElementById('canvas_id').toDataURL("image/png");
document.getElementById('id_hidden_image_field').value = dataURL;
And in django side:
add to the form a hidden field called 'hidden_image_field', which will be used to pass the data. this field is a simple CharField. you can add max_length limit to make sure image is in a reasonable size (note: not dimensions but actual size).
to parse the image data:
import re
import base64
dataUrlPattern = re.compile('data:image/(png|jpeg);base64,(.*)$')
ImageData = request.POST.get('hidden_image_field')
ImageData = dataUrlPattern.match(ImageData).group(2)
# If none or len 0, means illegal image data
if (ImageData == None or len(ImageData) == 0:
# PRINT ERROR MESSAGE HERE
pass
# Decode the 64 bit string into 32 bit
ImageData = base64.b64decode(ImageData)
and now ImageData contains the binary data, you can simply save to a file and it should work.
hope this helps.
Related
Sorry for the long question, but I wanted to express it as well as I could, and ask it in a way that is understood easily. I have a program that allows a user to crop an image using croppie.js in JavaScript, and send the image to a Hunchentoot server on the backend running a Lisp program. I am having an issue saving the Base64 image to a .png file once the user uploads it. Once the post request is sent to the server I am getting the Base64 image as a string, removing invalid characters from the Base64 request by creating a subsequence of the string without the heading sent by the post request and also substituting the "%" character for the "+" character to make the Base64 valid. Next I remove the substring +3D+3D at the end of my string, because the s-base64 library that I am using in Common Lisp complains +3D+3D is invalid padding, and I replace it with "==" which is considered valid padding. Next I create a byte array by using the s-base64 library to translate the Base64 string to a byte array, and store it in a variable. Then I loop through the byte array and write each byte to the output file. When that is finished I decided to print the end of the byte array to the console so that I could see if the output and ending padding is valid, which it appears to be. Here is that part of the code, with comments to make it clearer:
(define-easy-handler (handle-image :uri "/handle-image.html") ()
(let ((data-source (hunchentoot:raw-post-data :force-text t))) ;get Base64 string
(let ((new-string (subseq data-source 36))) ;create a subsequence of Base64 string
(let ((final-string (substitute #\+ #\% new-string))) ;substitute % for +
(let ((end (search "+3D+3D" final-string))) ;find the invalid padding
(setf final-string (concatenate 'string (subseq final-string 0 end) "==")) ;add valid padding
(let ((byte-array (with-input-from-string (in final-string) ;create byte array (or simple-vector) out of Base64 string
(decode-base64-bytes in))))
(with-open-file (out "path/path/path/path/profile-image.png" ;create output stream to store file
:direction :output
:if-exists :supersede
:element-type 'unsigned-byte)
(dotimes (i (length byte-array)) ;write each byte from the byte array to output stream
(write-byte (aref byte-array i) out)))) ;close stream
(format t "!!!!!!!!: ~a" (subseq final-string (- (length final-string) 30))))))) ;print ending to console to ensure proper padding
"Upload Successful") ;send response to client
And here is some of my JavaScript code:
$(document).ready(function(){
$image_crop = $('#image_demo').croppie({
enableExif: true,
viewport: {
width:200,
height:200,
type:'square' //circle
},
boundary:{
width:300,
height:300
}
});
As you can see, I first create the cropper. I allow the user to have a 200 x 200 square to crop, and the total size of the cropping space is 300 x 300. There are no issues with that part of the code:
$('#upload_image').on('change', function(){
var reader = new FileReader();
reader.onload = function (event) {
$image_crop.croppie('bind', {
url: event.target.result
}).then(function(){
console.log('jQuery bind complete');
});
}
Above I bind the image they've uploaded to the cropper (like if you upload a Facebook image), when they select a file. Again, no issues:
reader.readAsDataURL(this.files[0]);
$('#uploadImageModal').modal('show');
Above I read the file that they've selected and then the modal "pops up" as if you're cropping a Facebook or Instagram photo:
$('.crop_image').click(function(event){
$image_crop.croppie('result', {
type: 'canvas',
size: 'viewport'
}).then(function(response){
$.ajax({
url:"handle-image.html",
type: "POST",
data:{"image": response},
success:function(data){
$('#uploadImageModal').modal('hide');
$('#uploaded_image').html(data);
}
});
})
});
Above I upload the ajax request, and if the upload was successful they will get a message from the server that it was success, and I hide the modal for image cropping as well.
Now the issue is that the image is simply blank. I know that the Base64 is valid because I used a Base64 conversion tool to see if the string was valid. I also went back and did my research in regards to Bits, Bytes, Pixels, and dimensions of images to see how the computer interacts with them, so i'm not sure why my image is simply displaying blank. Here is a look at what the cropper looks like on my website:
The bind is working, and then I get the message that the upload was successful. BUT after writing the image and viewing it in the file system it is either a blank image, or will sometimes say that the image type is not supported.
The reason why I am tagging PHP in this post is because I am sure some people have had similar issues in PHP with uploading a cropped image via ajax, and some of those solutions might be applicable in this case, but obviously will require me translating the solution to the lisp syntax. My assumption is that something is wrong with my code when I translate the string to a byte array and write it to a file, but I thought it'd be good to post other sections of my code if I am overlooking something.
As Brad commented, you should first try to use binary uploads directly.
That aside: if you encounter a % in a base64-encoded string, it most likely means that the entire thing is additionally URL-encoded. A quick apropos search gave do-urlencode as a library to decode that. Replacing % with + makes valid base64, but the result does not necessarily represent valid jpg.
Also: use let* instead of nested let forms. Maybe use write-sequence instead of byte-wise output.
Thanks to the answer from #Brad and #Svante I was able to solve the problem. I decided to put the image that I want to upload within a form element, add the blob image from the canvas as FormData for the form, and then send the FormData via an ajax post request:
$('.crop_image').on('click mousedown touchstart', function(event){ //When the crop image button is pressed event.
$image_crop.croppie('result', { //Get the result of the cropper
size: 'viewport', //Set the size to the viewport size (180 x 120)
format: 'png', //.png format
type: 'blob'
}).then(function (blob){
$form = $('#uploadForm');
var fd = new FormData($form);
fd.append('upload_image', blob);
$.ajax({
url: "handle-image.html",
type: "POST",
data: fd,
processData: false,
contentType: false,
success: function (data){
$('#uploadImageModal').modal('hide');
$('#uploaded_image').html(data);
}
});
})
});
Here I just decided to change the croppie result from the type "canvas"to the type "form", and specify that it will be a .png file. From there I added the form data from my newly created form to the .ajax request and went from there. Thanks to #Brad and #Svante for the help here.
I am creating a webapp where the an image is shown to the user and the user can draw on the image (basically the image is in a canvas).
Now once the user is done drawing, the user will press the save button and the image needs to be saved in static folder (not locally). Since I am using Django to accomplish this; my request.FILES is always empty whenever I call the route.
My question is how can I save the canvas on which the user drew to a folder in the project and not locally.
index.html
<div id="uploaderForm">
<form enctype="multipart/form-data" action="{% url 'addImg' %}" method="post">
<input type="hidden" for="image_data" id="image_data" name="image_data" />
</form>
<div class="uploaderItem" id="uploaderSubmit">
Add to the gallery
</div>
</div>
script.js
function addImage()
{
console.log("In add image");
var image_data = canvas.toDataURL();
console.log("Image data:", image_data);
$('#image_data').val(image_data);
$('#uploaderForm > form').submit()
}
views.py
def add(request):
print request.FILES
#print request.POST['image_data']
return HttpResponse("You're looking at question")
the FILES array is reserved for files uploaded in file input fields through a multipart form. What you're doing is passing a base-64 encoded string representing your image, to a hidden text field that is then sent to your server. By your code, the image should be found here:
image_data = request.POST.get('image_data')
It will be a base-64 string, so you'll need to decode it, the example below can be applied to almost any data URL, but the format will depend on your browser, so it's kinda tricky:
import re
pattern = r'^data:(?P<mime_type>[^;]+);base64,(?P<image>.+)$'
result = re.match(pattern, image_data)
if result:
mime_type = result.group('mime_type')
image = result.group('image').decode('base64')
Be careful: by not using a file as transport, you are basically dumping the whole image in the server's memory, and that's expensive if you're planning to serve a lot of clients, and it's also time consuming, so your request could timeout before you are done with the image.
To fix this, you should consider uploading the image through AJAX, which is a supported way to treat Blobs in javascript, that way you could use Django's file upload facilities more efficiently
I have an ajax application where the PHP side sends uncoded raw image data, coming from a camera, to the client javascript side.
I would like to display the image via html and javascript using img or canvas tags.
Image data are 32*32 unsigned char.
I would like to do, whatever it takes to reach my goal (encoding or everythig else), but I want to do it at client side beacause I cannot handle any other operation at the server side.
I tried to encode raw data into jpeg or png data but without success.
I post an example that doesn't work:
var encoder = new JPEGEncoder(9);
var jpgFile = encoder.encode(rawImage, 9);
document.getElementById("image").src = jpgFile;
jpgFile is like
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAFk9Q05DOFlOSE5kXllphd6QhXp6hf/CzaHe//////////////////////////////////////////////////8BXmRkhXWF/5CQ///////////////////////////////////////////////////////////////////////////AABEIAAAAAAMBEQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AP8A/9k=
I got JPEGEncoder googling on internet.
Tried it for you =)
http://jsfiddle.net/bYGum/
All you have to do is make sure your string is base64 encoded and do the exact same thing you have already written:
document.getElementById("image").src = jpgFile;
In my jsp page, I have a manipulated photo within a html5 canvas and I want to upload it to facebook upon clicking the upload button.
FacebookType publishPhotoResponse = facebookClient.publish("me/photos", FacebookType.class,
BinaryAttachment.with("test.jpeg", getClass().getResourceAsStream("xxx")),
Parameter.with("message", "Test"));
out.println("Published photo ID: " + publishPhotoResponse.getId());
I am using Restfb for the uploading part in my servlet. However, I have no clue what attribute is needed for me to pass over to servlet side for processing (for example: "xxx"). When I used toDataURL, the URL of the image is base64. Does facebook api allows me to upload photo using the base64 format?
var base64URL = document.getElementById('canvasImg').src;
var decodedURL = escape(window.atob(base64URL));
alert(decodedURL);
The above coding seems to contain error as it won't display the alert. Should I decode the base64 data first before handling it over to servlet or should I pass the whole base64 data to servlet to handle the decoding?
I am developing an a drawing application using Javascript.
Users will be able to draw on a canvas. Once they are done with drawing, they will be able to convert it into an image (Convert to image button).
This is the code:
function putImage()
{
var canvas1 = document.getElementById("canvas");
if (canvas1.getContext)
{
var ctx = canvas1.getContext("2d");
var myImage = canvas1.toDataURL("image/png");
}
var imageElement = document.getElementById("MyPix");
imageElement.src = myImage;
$('#submit_btn').closest('.ui-btn').show();
}
There's a submit button and when the users click on it, the application will redirect to another page whereby the user will be able to send an email (using java mail) with the image attached to it.
The page allows user to type in the email address that they wanna send to, and the body of the email.
May i know how to make the image auto-attach to the email so that the after the user type in the email address and the body, they will be able to send the mail?
Thanks in advance!
To send the image as an attachment with javamail, you need the bytes from say a jpg or bmp. What you'll need to do is send the model, eg the coordinates, to the server and recreate the image server side. Perhaps a html5 canvas has direct support for outputting the images as bytes, I don't know, but that would help. In that case you'd simply transfer those bytes to the server for attaching to the mail.
HTML5 Canvas has a cool api trick to do this:
var encodedImage = canvas1.toDataURL(); //this generates base64 encoded image in png
//for jpeg
var encodedImage = canvas1.toDataURL("image/jpeg");
Now you can store that encodedImage in back-end in a table or file. If you want to show it on a page, just assign it back to html img tag to source property