I am using ember uploader in order to upload files directly to Amazon S3 from my client once I have signed the image upload on the server as described here
Now what I am trying to do is resize the image to create a thumbnail before uploading to Amazon S3. This way I'll have two images to upload to Amazon S3. One will be the original image and the other will be the thumbnail created.
At the moment I am using a HTML 5 canvas in order to resize the image:
var MAX_HEIGHT = 300;
function render(src){
var image = new Image();
image.onload = function(){
var canvas = document.getElementById("myCanvas");
if(image.height > MAX_HEIGHT) {
image.width *= MAX_HEIGHT / image.height;
image.height = MAX_HEIGHT;
}
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0, image.width, image.height);
};
image.src = src;
}
I am able to resize the image in the HTML 5 canvas. The problem I am facing is that I am battling to then get the image back into a File Object for the Ember Uploader to sign and send over to Amazon S3.
This is the code that handles the actual upload:
if (!Ember.isEmpty(files)) {
uploader.upload(files[0]); // Uploader will send a sign request then upload to S3
}
As you can see, the first item of the FileList array is being uploaded. I would like to be able to append the canvas' thumbnail image to this FileList in order to iterate over the FileList and then sign and upload both images to Amazon S3.
I know that a canvas can be converted to a data url using canvas.toDataURL but is it possible to covert a canvas to a File Object or should I do the resize and upload in a different way?
Thanks!
Related
So, I have a user input which serve to upload pictures. This is a simple example:
function handleImage(e){
var reader = new FileReader();
reader.onload = function(event){
var img = new Image();
img.onload = function(){
console.log (img);
}
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
}
<input type="file" onchange="handleImage(event)"><br>
As you can see, I display an Image () on the console. I want to convert this Image into a jpg file.
I understood how to get the pixels of the picture but it's completely crazy to send it to a server: It's to large!
I also tried to access to the jpg file stored in the computer but I do not achieve to anything.
The only way I found is to send it with a form like this:
<form action="anything.php" method="post" enctype="multipart/form-data">
<input type="file" name="fileToUpload" id="fileToUpload">
</form>
And in PHP:
$_FILES["fileToUpload"]["tmp_name"]
Why not with JS?
My final goal is to send the jpg file with AJAX.
Tell me if you have some questions.
The simplest way is to use a canvas element and then invoke a download action allowing the user to select where to save the image.
You mention that the image is large, but not how much - be aware that with canvas you will also run into restrictions when the image source starts to touch around the 8k area in pixel size.
A simplified example (IE will require a polyfill for toBlob()).:
Load image source via input
Use File blob directly as image source via URL.createObjectURL()
When loaded, create a temporary canvas, set canvas size = image size and draw in image
Use toBlob() (more efficient on memory and performance and require no transcoding to/from Base64) to obtain a Blob.
We'll convert the Blob to File (a subset object and it will reference the same memory) so we can also give a filename as well as (important!) a binary mime-type.
Since the mime-type for the final step is binary the browser will invoke a Save as dialog.
document.querySelector("input").onchange = function() {
var img = new Image;
img.onload = convert;
img.src = URL.createObjectURL(this.files[0]);
};
function convert() {
URL.revokeObjectURL(this.src); // free up memory
var c = document.createElement("canvas"), // create a temp. canvas
ctx = c.getContext("2d");
c.width = this.width; // set size = image, draw
c.height = this.height;
ctx.drawImage(this, 0, 0);
// convert to File object, NOTE: we're using binary mime-type for the final Blob/File
c.toBlob(function(blob) {
var file = new File([blob], "MyJPEG.jpg", {type: "application/octet-stream"});
window.location = URL.createObjectURL(file);
}, "image/jpeg", 0.75); // mime=JPEG, quality=0.75
}
// NOTE: toBlob() is not supported in IE, use a polyfill for IE.
<label>Select image to convert: <input type=file></label>
Update: If you just are after a string (base-64 encoded) version of the newly created JPEG simply use toDataURL() instead of toBlob():
document.querySelector("input").onchange = function() {
var img = new Image;
img.onload = convert;
img.src = URL.createObjectURL(this.files[0]);
};
function convert() {
URL.revokeObjectURL(this.src); // free up memory
var c = document.createElement("canvas"), // create a temp. canvas
ctx = c.getContext("2d");
c.width = this.width; // set size = image, draw
c.height = this.height;
ctx.drawImage(this, 0, 0);
// convert to File object, NOTE: we're using binary mime-type for the final Blob/File
var jpeg = c.toDataURL("image/jpeg", 0.75); // mime=JPEG, quality=0.75
console.log(jpeg.length)
}
<label>Select image to convert: <input type=file></label>
JavaScript on the client side can not save files.
You have multiple options:
Render the image on an <canvas> element. This way it can be saved with right click -> save image
Inset the image as <img> element. This way it can be saved with right click -> save image
Send the image data as base64 string to the server. Do the processing there
Use a server side language like PHP or Node.js to save the file
Long story short, you have to use some server side logic to save the file on disk
My site allows users to upload their own photos. I've learned that this is trickier than first expected and although I can get each of the components working individually I'm not successfully getting them to produce the outcome I need when combining them. I believe the issue is my lack of understanding around the outputs of the functions i'm using.
I think what I need to do is:
Let the user choose the file to upload
Check / fix the orientation
Display the image in a canvas
Convert the canvas to base64 (to upload to Firebase)
Upload the item
Let the user choose the file to upload
$(document).on('change','#fileUpload',function(e) {
correctImageOrientation(e);
});
Check / fix the image orientation
I'm using image-load (link) for my orientation changes.
function correctImageOrientation(e) {
loadImage(
e.target.files[0],
function (img) {
// document.body.appendChild(img); // Note: this successfully appends the image in the correct orientation to the body - but I need to do this to a canvas to I call:
addImageToCanvas(img);
},
{
orientation: true
}
);
Display the image in a canvas
This part fails. If I pass in the selected file using e.target.files[0] and create an objectURL then it works. This is why I think the img i'm passing in needs to be read / displayed on the canvas differently.
function addImageToCanvas(img) {
img.onload = function() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, 300, 300);
console.log("Ending img.onload"); // This is never reached
}
}
I can then successfully convert the file to base64 and upload to Firebase.
I think there must be an easier way to achieve what I want which is for people to upload an image, for the orientation to be correct, and for this to be stored online.
EDIT: To store the image in firebase I am using this to convert the image back to a blob.
I have replaced addImageToCanvas with the function below which was taken then modified from David Walsh's site. Because I'm passing in an image I needed to convert this to a canvas.
function convertImageToCanvas(image) {
var canvas = document.getElementById("canvas");
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
}
I want to avoid uploading big/heavy image files.
I prefer HTML5 FileAPI library for this job.
All other functionality is added (upload, re-order, etc.), so I only need the image-resize function.
CASE:
On the page there is an input for multiple files.
On input change event (when adding files), resize the entered images/files and append them to FormData(), then send the FormData() to PHP script via ajax.
EXAMPLE:
$('input').on('change',function(){
var formData = resizeImages(this.files[0]);
uploadResizedImages(formData);
});
function resizeImages(files){
formData = new FormData();
//For each image, resize it and append to formData
//resize file part missing....
formData.append('files[]',this);//Appending to formData (this = currently iterated file)
return formData;//Return the formData with resized images
}
Anyone?
Thanks
In my experience you cannot manipulate the image on the client side then upload the manipulated image in tact via a file input in a form.
The way I have done what you are trying to do in the past involves a few steps.
Select image using a file input
Read the file as a dataURL
Use canvas to manipulate the image as needed
Export the new image as a dataUrl
Use ajax to upload the image to the server as a dataURL
Use server side functions to convert the dataUrl to an image and store
https://jsfiddle.net/0hmhumL1/
function resizeInCanvas(img){
///////// 3-3 manipulate image
var perferedWidth = 2700;
var ratio = perferedWidth / img.width;
var canvas = $("<canvas>")[0];
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0,0,canvas.width, canvas.height);
//////////4. export as dataUrl
return canvas.toDataURL();
}
When uploading as a dataUrl you increase the size (bandwidth required) of the manipulated image by about 20% so you may not see the savings you are looking for unless you are changing the image size considerably.
After seing alot of examples here, and try to use them they work in some degree, but it doesn't true convert the image.
The image as the extension now png and open as such, but if I go into properties it stills says :
on C# and HTML the image shows without any problem, but I'm using the language c++ and it won't show on the QPushButton
This is an example of the code that I'm using on the javascript to convert the image:
// Converts image to canvas; returns new canvas element
function convertImageToCanvas(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
return canvas;
}
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
So the image is saved throught base64 to png opens as such on many programs but on the one that I'm trying to do it wont.
How to I properly convert an image to png ?! Since this method isn't working
I want to be able to take a base64 encoded string and mimic it as if it were a file that i selected it from the "browse" feature. Here is a short example:
<input type="file" id="file" />
<script>
//I want to be able to access it here
$("#file").files;
</script>
I want to be able to access the files array and use it as a normal file. I currently get the base64 encoded string like so:
// resize the canvas and draw the image data into it
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
preview.appendChild(canvas); // do the actual resized preview
var base64String = canvas.toDataURL("image/jpeg", 0.7);
File elements are only usable by the user, you would need to transfer the base64 string as normal form submission and then decode it on the server.