Javascript Client Side JPEG image compression - javascript

I want to basically compress images uploaded (client-side) and then attach the src to my id (of html). The code of the function is as below:
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
console.log("e.target.result",e.target.result);
var imageEle = new Image();
imageEle.src = e.target.result;
imageEle.onload = function() {
var cvs = document.createElement('canvas');
cvs.width = imageEle.naturalWidth;
cvs.height = imageEle.naturalHeight;
var ctx = cvs.getContext("2d").drawImage(imageEle,0,0);
var newImageData = cvs.toDataURL('image/jpeg',0.5);
$('#uploadedImage').attr('src', newImageData);
};
};
reader.readAsDataURL(input.files[0]);
}
}
This should work fine, but what happens is the uploaded image after process appears to be a black box. Now I have read alot of similar questions, but in relation to compression I am not able to fix this issue. Also this works good with files (jpeg images) less than 500 Kb, but for real files that are 1 MB plus it gives a black box as data uri. It would be awesome if somebody could help me with this.
Thanks,
Vaibhav

There's a size limit to toDataURL use, see canvas.toDataURL() for large canvas for a similar question, and possible answers with server-side code.
If you're restricted to this client-side code, you'll have to reduce the size of the image before using toDataURL.

Related

FileReader() is unable to read large files

I'm using the following approach in order to preview images before uploading them:
$("#file").change(function() {
var reader = new FileReader();
reader.readAsArrayBuffer(this.files[0]);
var fileName = this.files[0].name;
var fileType = this.files[0].type;
alert(fileType)
reader.onloadend = function() {
var base64Image = btoa(String.fromCharCode.apply(null, new Uint8Array(this.result)));
// I show the image now and convert the data to base 64
}
}
I have noticed that when the image is large, the method fails and I cannot preview the image.
I am unsure if the problem is due to base64 conversion or the FileReader.
Is there any setting to increase the max size, or is there any work around?
Here is the error message thrown in the console :
Uncaught RangeError: Maximum call stack size exceeded
at FileReader.reader.onloadend
Your problem is that you use Function.apply which will convert your Typed Array items to arguments to the String.fromCharCode method.
Functions have a maximum arguments length limit.
To avoid this, when dealing with large files, the best way is to not process it at all.
If you need to send the file to your server, simply send the Blob directly, this can be easily achieved with the FormData API.
If you need to display the file i.e in HTML media element, then use URL.createObjectURL(yourFile) method.
And if you really need a dataURI version of the file, then use reader.readAsDataURL(yourFile) method.
Works for me:
var reader = new FileReader();
reader.onload = function (evt) {
var binary = '';
var bytes = new Uint8Array(reader.result);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
console.log(btoa(binary))
}
reader.readAsArrayBuffer(file)
If you read the file using the FileReader, the whole file will be loaded into the memory. If you'd like handle large files, this will simply result in your web browser crashing right away. If you are really interested in passing your file as a Base64 String, I recommend you to add file size constraints in order to prevent any potential problems. As a conclusion, none of the methods of the FileReader class would be suitable for this purpose unless and again unless you are dealing with small files not larger than 100MG or so, otherwise you will run into problems.
After playing around here's the solution:
$("#file").change(function () {
var reader = new FileReader();
reader.readAsBinaryString(this.files[0]);
var fileName = this.files[0].name;
var fileType = this.files[0].type;
alert(fileType)
reader.onloadend = function () {
var base64Image = btoa(this.result);
}
}

how do i get any images in binary code using js?

I want to make a multiple images upload system with prograss bar. I want to do with simaple code(using jquery or js). I want when user has upload his images on browser and i want to show on browser that images and with upload button he starts uploading image via ajax in his folder.
So questions
1.) Is it possible to show uploaded image (without any complicated code) ?
2.) Do i get a variable or array where uploaded images are stored as base64 code (data:/img:dfd5d/d54fs..... something like this) or encoded?
3.) How do i add progressBar ?
I didn't write any code yet because i dont know how to start. I am new in computer science.
But i find this code on this site
function previewFile() {
var preview = document.querySelector('img');
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
reader.onloadend = function () {
preview.src = reader.result;
}
if (file) {
reader.readAsDataURL(file);
} else {
preview.src = "";
}
}
This is easy code and i understand but one thing is not clear what does mean this line var reader = new FileReader(); why use new and what is it ?
Ty in advance and please dont explain complicate and i am not very good in english. So please try to explain in poor words if possible..
Assuming that you have this field
<input type="file" onchange="showImage(this)"/>
you can create a script to take the binary data and show it
function showImage(input){
var reader = new FileReader();
// validating...
var fileType = input.files[0].type;
var filesize = input.files[0].size;
// filetype (this will validate mimetype, only png, jpeg, jpg allowed)
var fileTypes = ["image/png", "image/jpeg", "image/gif"];
if (fileTypes.indexOf(fileType) < 0){
// return error, invalid mimetype
return false;
}
// file cannot be more than 500kb
if (filesize > 5000000) {
// return error, image too big
return false;
}
reader.onload = function (e) {
// e will contain the image info
jQuery('#myimagetopreview').attr('src', e.target.result)
}
reader.readAsDataURL(input.files[0]);
}
This should work, if you have problem tell me
edit: FileReader is not supported by all the browsers, check the documentation for more https://developer.mozilla.org/en/docs/Web/API/FileReader
The FileReader in JS has Status "Working Draft" and isn't part of the official JS API. I think you have to wait until the Browsers support this ne API or you have to activate experimental JS API in the Browser.

angularjs compress image before upload

I'm buliding a web site for mobile devices, that uses angular-file-upload.min.js for uploading images from a mobile device image library.
html code:
<div>
<div class="rating-camera-icon">
<input type="file" accept="image/*" name="file" ng-file-
select="onFileSelect($files)">
</div>
<img ng-show="fileName" ng-src="server/{{fileName}}" width="40"
style="margin-left:10px">
</div>
code:
$scope.onFileSelect = function($files) {
for (var i = 0; i < $files.length; i++) {
var file = $files[i];
if (!file.type.match(/image.*/)) {
// this file is not an image.
};
$scope.upload = $upload.upload({
url: BASE_URL + 'upload.php',
data: {myObj: $scope.myModelObj},
file: file
}).progress(function(evt) {
// console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
// $scope.fileProgress = evt.loaded / evt.total * 100.0;
}).success(function(data, status, headers, config) {
// file is uploaded successfully
$scope.fileName = data;
});
}
};
The upload is very slow in mobile devices. How can I compress the file?
Stringifying the image into a base-64 text format is all fine and well, but it will take a small amount of time and certainly does not compress it. In fact it will likely be noticeably larger than the raw image. Unfortunately your browser will also not gzip an uploads. They can of course handle gzipped downloads. You could certainly try to do a gzip of the text itself using some pure JS solution. Looking on github you can find such things - https://github.com/beatgammit/gzip-js However, that will take some time as well and there is no guarantee that the compressed text version of the image is any smaller than the raw JPEG you attach.
A native mobile app might decide to use some native code JPEG or PNG optimization before sending (basically resample the image) if appropriate, but doing this out in JavaScript seems potentially problematic at this point in time. Given Atwood's law (of writing everything eventually in JavaScript) it certainly could be done but at this point in mid-2014 it isn't.
You could try to store the image on a canvas, then convert to data64 and then upload the data string.
I made kind of this in a POC, theres a bug in ios regarding large images as the one you could take with the camera when in canvas, but the overal works nice... something like;
file = files[0];
try {
var URL = window.URL || window.webkitURL,
imgURL = URL.createObjectURL(file);
showPicture.src = imgURL;
imgBlobToStore = imgURL;
if(AppData.supports_html5_storage()) {
var canvas = document.getElementById('storingCanvas') ,
ctx = canvas.getContext('2d'),
img = new Image(),
convertedFile;
img.src = imgBlobToStore;
img.onload = function () {
canvas.width = img.width;
canvas.height= img.height;
ctx.drawImage(img, 0,0,img.width, img.height);
convertedFile = canvas.toDataURL("image/jpeg"); //or png
//replace with angular storage here
localStorage.setItem( $('.pic').attr('id'), convertedFile);
};
},
}
There are several libraries that do this for you on the client side.
https://github.com/oukan/angular-image-compress
https://github.com/sammychl/ng-image-compress
http://angularscript.com/client-side-image-compress-directive-with-angular/
As an alternative to a programmatic solution - if your image is being created by the device camera for upload, then why not simply change the resolution of the camera. The smallest resolution may be 10x smaller than the largest, and this may be suitable for many situations.

Bad image rotation

I have an issue with JavaScript when rendering an image before upload in a correct rotation. It seems that when you render the image witch have the correct rotation only on exif data the browser doesn't use it.
Users see a different rotation between what they have on their system on when image is displayed on the website by JavaScript.
The code is very basic:
Do you know a simple way to correct this rotation bug ?
LbEmeraude.handleImage = function (f) {
if (f.type.match('image.*')) {
var reader = new FileReader();
reader.onload = (function (file) {
return function (e) {
var image = {};
image.dataAsUrl = e.target.result;
LbEmeraude.renderImage(image);
};
})(f);
var image = reader.readAsDataURL(f);
}
}
LbEmeraude.renderImage = function (image) {
var eImage = LbEmeraude.createImgElement(image.dataAsUrl);
$('someElement').append(eImage);
};
LbEmeraude.createImgElement = function (src) {
var image = document.createElement("img");
image.src = src;
return image;
}
Thank for your attention.
What you are asking for is nothing new... check this out: https://bugzilla.mozilla.org/show_bug.cgi?id=298619
That sucker was opened in 2005 and has not been resolved yet. This article is old but really robust: http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/
But the key part in there is kinda far down where he notes that the browser does not usually apply exif rotation when in the context of an html img tag, but may honor it when opening the image in its own tab.
So right now no browser will do it by default, the web apps that seem to do it are mostly getting that value on the server and serving down different assets.
But it looks like there is hope if you want to hack it in: Accessing JPEG EXIF rotation data in JavaScript on the client side

JavaScript: converting pdf to bytes array and rendering the pdf in UI using HTML5

I need the help on the below two things
1) I need to convert the PDF file data to bytes arrray in JavaScript .
2) Using the above bytes array, I need to render it in the UI as PDF file.
Questions may look like, why I want to convert the PDF file to bytes Stream and again why I want to show it as PDF in UI. But I need to figure out a way for the above two which helps me to solve many issues in my project.
Any suggestions for reading or solutions to above problems will be much appreciable.
Thanks for your time!
please check pdf.js.
it may be helpful
http://mozilla.github.io/pdf.js
Following code will read the file locally and render the PDF. This will be faster as File doesn't have to get uploaded to server and get downloaded to browser again.
<script type="text/javascript">
//Workaround for a bug on IE.
PDFJS.workerSrc = "pdf.worker.js";
//File from the input element
inputElement.onchange = function(event) {
var file = event.target.files[0];
//Read the file locally using file reader
var fileReader = new FileReader();
fileReader.onload = function() {
var typedarray = new Uint8Array(this.result);
// Render PDF
PDFJS.getDocument(typedarray).then(function(pdf) {
pdf.getPage(1).then(function getPageHelloWorld(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({canvasContext: context, viewport: viewport});
});
});
};
// Read the file into array buffer.
fileReader.readAsArrayBuffer(file);
}
</script>

Categories

Resources