As part of a web app, once images have been downloaded and rendered on a web page, I need to determine an image's file size (kb) and resolution within the browser context (so I could, for example, display that info on the page. This needs to be done client-side, obviously. Must be able to be solved x-browser without an ActiveX control or Java applet (IE7+, FF3+, Safari 3+, IE6 nice to have), though it doesn't have to be the same solution per browser.
Ideally this would be done using system Javascript, but if I absolutely need a JQuery or similar library (or a tiny subset of it), that could be done.
Edit:
To get the current in-browser pixel size of a DOM element (in your case IMG elements) excluding the border and margin, you can use the clientWidth and clientHeight properties.
var img = document.getElementById('imageId');
var width = img.clientWidth;
var height = img.clientHeight;
Now to get the file size, now I can only think about the fileSize property that Internet Explorer exposes for document and IMG elements...
Edit 2: Something comes to my mind...
To get the size of a file hosted on the server, you could simply make an HEAD HTTP Request using Ajax. This kind of request is used to obtain metainformation about the url implied by the request without transferring any content of it in the response.
At the end of the HTTP Request, we have access to the response HTTP Headers, including the Content-Length which represents the size of the file in bytes.
A basic example using raw XHR:
var xhr = new XMLHttpRequest();
xhr.open('HEAD', 'img/test.jpg', true);
xhr.onreadystatechange = function(){
if ( xhr.readyState == 4 ) {
if ( xhr.status == 200 ) {
alert('Size in bytes: ' + xhr.getResponseHeader('Content-Length'));
} else {
alert('ERROR');
}
}
};
xhr.send(null);
Note: Keep in mind that when you do Ajax requests, you are restricted by the Same origin policy, which allows you to make requests only within the same domain.
Check a working proof of concept here.
Edit 3:
1.) About the Content-Length, I think that a size mismatch could happen for example if the server response is gzipped, you can do some tests to see if this happens on your server.
2.) For get the original dimensions of a image, you could create an IMG element programmatically, for example:
var img = document.createElement('img');
img.onload = function () { alert(img.width + ' x ' + img.height); };
img.src='http://sstatic.net/so/img/logo.png';
Check the uploaded image size using Javascript
<script type="text/javascript">
function check(){
var imgpath=document.getElementById('imgfile');
if (!imgpath.value==""){
var img=imgpath.files[0].size;
var imgsize=img/1024;
alert(imgsize);
}
}
</script>
Html code
<form method="post" enctype="multipart/form-data" onsubmit="return check();">
<input type="file" name="imgfile" id="imgfile"><br><input type="submit">
</form>
Getting the Original Dimensions of the Image
If you need to get the original image dimensions (not in the browser context), clientWidth and clientHeight properties do not work since they return incorrect values if the image is stretched/shrunk via css.
To get original image dimensions, use naturalHeight and naturalWidth properties.
var img = document.getElementById('imageId');
var width = img.naturalWidth;
var height = img.naturalHeight;
p.s. This does not answer the original question as the accepted answer does the job. This, instead, serves like addition to it.
How about this:
var imageUrl = 'https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg';
var blob = null;
var xhr = new XMLHttpRequest();
xhr.open('GET', imageUrl, true);
xhr.responseType = 'blob';
xhr.onload = function()
{
blob = xhr.response;
console.log(blob, blob.size);
}
xhr.send();
http://qnimate.com/javascript-create-file-object-from-url/
due to Same Origin Policy, only work under same origin
Regarding the width and height:
var img = document.getElementById('imageId');
var width = img.clientWidth;
var height = img.clientHeight;
Regarding the filesize you can use performance
var size = performance.getEntriesByName(url)[0];
console.log(size.transferSize); // or decodedBodySize might differ if compression is used on server side
Service workers have access to header informations, including the Content-Length header.
Service workers are a bit complicated to understand, so I've built a small library called sw-get-headers.
Than you need to:
subscribe to the library's response event
identify the image's url among all the network requests
here you go, you can read the Content-Length header!
Note that your website needs to be on HTTPS to use Service Workers, the browser needs to be compatible with Service Workers and the images must be on the same origin as your page.
Most folks have answered how a downloaded image's dimensions can be known so I'll just try to answer other part of the question - knowing downloaded image's file-size.
You can do this using resource timing api. Very specifically transferSize, encodedBodySize and decodedBodySize properties can be used for the purpose.
Check out my answer here for code snippet and more information if you seek : JavaScript - Get size in bytes from HTML img src
You can use generic Image object to load source dynamically then measure it:
const img = new Image();
img.src = this.getUrlSource()
img.onload = ({target}) =>{
let width = target.width;
let height = target.height;
}
You can get the dimensions using getElement(...).width and ...height.
Since JavaScript can't access anything on the local disk for security reasons, you can't examine local files. This is also true for files in the browser's cache.
You really need a server which can process AJAX requests. On that server, install a service that downloads the image and saves the data stream in a dummy output which just counts the bytes. Note that you can't always rely on the Content-length header field since the image data might be encoded. Otherwise, it would be enough to send a HTTP HEAD request.
You can find dimension of an image on the page using something like
document.getElementById('someImage').width
file size, however, you will have to use something server-side
var img = new Image();
img.src = sYourFilePath;
var iSize = img.fileSize;
The only thing you can do is to upload the image to a server and check the image size and dimension using some server side language like C#.
Edit:
Your need can't be done using javascript only.
Related
My code is working very well on my localhost but it is not working on the site.
I got this error from the console, for this line .getImageData(x,y,1,1).data:
Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.
part of my code:
jQuery.Event.prototype.rgb=function(){
var x = this.offsetX || (this.pageX - $(this.target).offset().left),y = this.offsetY || (this.pageY - $(this.target).offset().top);
if (this.target.nodeName!=="CANVAS")return null;
return this.target.getContext('2d').getImageData(x,y,1,1).data;
}
Note: my image url (src) is from a subdomain url
As others have said you are "tainting" the canvas by loading from a cross origins domain.
https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image
However, you may be able to prevent this by simply setting:
img.crossOrigin = "Anonymous";
This only works if the remote server sets the following header appropriately:
Access-Control-Allow-Origin "*"
The Dropbox file chooser when using the "direct link" option is a great example of this. I use it on oddprints.com to hoover up images from the remote dropbox image url, into my canvas, and then submit the image data back into my server. All in javascript
I found that I had to use .setAttribute('crossOrigin', '') and had to append a timestamp to the URL's query string to avoid a 304 response lacking the Access-Control-Allow-Origin header.
This gives me
var url = 'http://lorempixel.com/g/400/200/';
var imgObj = new Image();
imgObj.src = url + '?' + new Date().getTime();
imgObj.setAttribute('crossOrigin', '');
You won't be able to draw images directly from another server into a canvas and then use getImageData. It's a security issue and the canvas will be considered "tainted".
Would it work for you to save a copy of the image to your server using PHP and then just load the new image? For example, you could send the URL to the PHP script and save it to your server, then return the new filename to your javascript like this:
<?php //The name of this file in this example is imgdata.php
$url=$_GET['url'];
// prevent hackers from uploading PHP scripts and pwning your system
if(!#is_array(getimagesize($url))){
echo "path/to/placeholderImage.png";
exit("wrong file type.");
}
$img = file_get_contents($url);
$fn = substr(strrchr($url, "/"), 1);
file_put_contents($fn,$img);
echo $fn;
?>
You'd use the PHP script with some ajax javascript like this:
xi=new XMLHttpRequest();
xi.open("GET","imgdata.php?url="+yourImageURL,true);
xi.send();
xi.onreadystatechange=function() {
if(xi.readyState==4 && xi.status==200) {
img=new Image;
img.onload=function(){
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
img.src=xi.responseText;
}
}
If you use getImageData on the canvas after that, it will work fine.
Alternatively, if you don't want to save the whole image, you could pass x & y coordinates to your PHP script and calculate the pixel's rgba value on that side. I think there are good libraries for doing that kind of image processing in PHP.
If you want to use this approach, let me know if you need help implementing it.
edit-1: peeps pointed out that the php script is exposed and allows the internet to potentially use it maliciously. there are a million ways to handle this, one of the simplest being some sort of URL obfuscation... i reckon secure php practices deserves a separate google ;P
edit-2: by popular demand, I've added a check to ensure it is an image and not a php script (from: PHP check if file is an image).
I was seeing this error on Chrome while I was testing my code locally. I switched to Firefox and I am not seeing the error any more. Maybe switching to another browser is a quick fix.
If you are using the solution given in the first answer, then make sure you add img.crossOrigin = "Anonymous"; just after you declare the img variable (for eg. var img = new Image();).
When working on local, add a server.
I had a similar issue when working on local. Your URL is going to be the path to the local file, for example, file:///Users/PeterP/Desktop/folder/index.html.
Please note that I am on a Mac.
I got around this by installing an HTTP server globally. I used https://www.npmjs.com/package/http-server
Steps
Global install: npm install http-server -g
Run server: http-server ~/Desktop/folder/
These steps assume that you have node installed, otherwise you won't get very far running npm commands.
My problem was so messed up I just base64 encoded the image to ensure there couldn't be any CORS issues
Your problem is that you load an external image, meaning from another domain. This causes a security error when you try to access any data of your canvas context.
You are "tainting" the canvas by loading from a cross origins domain. Check out this MDN article:
https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image
Set the Image's crossOrigin attribute to Anonymous.
let image = new Image();
// image.src = ...;
image.crossOrigin = `Anonymous`;
As matt burns says in his answer, you may need to enable CORS on the server where the problem images are hosted.
If the server is Apache, this can be done by adding the following snippet (from here) to either your VirtualHost config or an .htaccess file:
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>
...if adding it to a VirtualHost, you'll probably need to reload Apache's config too (eg. sudo service apache2 reload if Apache's running on a Linux server)
you can convert the image to a data string as use the image source instead of the actual image source.
[https://www.base64-image.de/][1] to convert image to data string.
Convert and copy string data from the above website.
set image.src = <copied_data_string>.
workaround solution,
convert source image URL to Base64 data and assign to img
for example, use Axios
const getBase64 = async(url)=>{
try {
let image = await axios.get(url, { responseType: 'arraybuffer' });
let raw = Buffer.from(image.data).toString('base64');
return "data:" + image.headers["content-type"] + ";base64,"+raw;
} catch (error) {
console.log(error)
}
}
var image = new Image()
image.src=getBase64(url)
no cross-origin dependency from canvas
I meet the same problem today, and solve it by the code follows.
html code:
<div style='display: none'>
<img id='img' src='img/iak.png' width='600' height='400' />
</div>
<canvas id='iak'>broswer don't support canvas</canvas>
js code:
var canvas = document.getElementById('iak')
var iakImg = document.getElementById('img')
var ctx = canvas.getContext('2d')
var image = new Image()
image.src=iakImg.src
image.onload = function () {
ctx.drawImage(image,0,0)
var data = ctx.getImageData(0,0,600,400)
}
code like above, and there is no cross-domain problem.
I was having the same issue, and for me it worked by simply concatenating https:${image.getAttribute('src')}
As part of a web app, once images have been downloaded and rendered on a web page, I need to determine an image's file size (kb) and resolution within the browser context (so I could, for example, display that info on the page. This needs to be done client-side, obviously. Must be able to be solved x-browser without an ActiveX control or Java applet (IE7+, FF3+, Safari 3+, IE6 nice to have), though it doesn't have to be the same solution per browser.
Ideally this would be done using system Javascript, but if I absolutely need a JQuery or similar library (or a tiny subset of it), that could be done.
Edit:
To get the current in-browser pixel size of a DOM element (in your case IMG elements) excluding the border and margin, you can use the clientWidth and clientHeight properties.
var img = document.getElementById('imageId');
var width = img.clientWidth;
var height = img.clientHeight;
Now to get the file size, now I can only think about the fileSize property that Internet Explorer exposes for document and IMG elements...
Edit 2: Something comes to my mind...
To get the size of a file hosted on the server, you could simply make an HEAD HTTP Request using Ajax. This kind of request is used to obtain metainformation about the url implied by the request without transferring any content of it in the response.
At the end of the HTTP Request, we have access to the response HTTP Headers, including the Content-Length which represents the size of the file in bytes.
A basic example using raw XHR:
var xhr = new XMLHttpRequest();
xhr.open('HEAD', 'img/test.jpg', true);
xhr.onreadystatechange = function(){
if ( xhr.readyState == 4 ) {
if ( xhr.status == 200 ) {
alert('Size in bytes: ' + xhr.getResponseHeader('Content-Length'));
} else {
alert('ERROR');
}
}
};
xhr.send(null);
Note: Keep in mind that when you do Ajax requests, you are restricted by the Same origin policy, which allows you to make requests only within the same domain.
Check a working proof of concept here.
Edit 3:
1.) About the Content-Length, I think that a size mismatch could happen for example if the server response is gzipped, you can do some tests to see if this happens on your server.
2.) For get the original dimensions of a image, you could create an IMG element programmatically, for example:
var img = document.createElement('img');
img.onload = function () { alert(img.width + ' x ' + img.height); };
img.src='http://sstatic.net/so/img/logo.png';
Check the uploaded image size using Javascript
<script type="text/javascript">
function check(){
var imgpath=document.getElementById('imgfile');
if (!imgpath.value==""){
var img=imgpath.files[0].size;
var imgsize=img/1024;
alert(imgsize);
}
}
</script>
Html code
<form method="post" enctype="multipart/form-data" onsubmit="return check();">
<input type="file" name="imgfile" id="imgfile"><br><input type="submit">
</form>
Getting the Original Dimensions of the Image
If you need to get the original image dimensions (not in the browser context), clientWidth and clientHeight properties do not work since they return incorrect values if the image is stretched/shrunk via css.
To get original image dimensions, use naturalHeight and naturalWidth properties.
var img = document.getElementById('imageId');
var width = img.naturalWidth;
var height = img.naturalHeight;
p.s. This does not answer the original question as the accepted answer does the job. This, instead, serves like addition to it.
How about this:
var imageUrl = 'https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg';
var blob = null;
var xhr = new XMLHttpRequest();
xhr.open('GET', imageUrl, true);
xhr.responseType = 'blob';
xhr.onload = function()
{
blob = xhr.response;
console.log(blob, blob.size);
}
xhr.send();
http://qnimate.com/javascript-create-file-object-from-url/
due to Same Origin Policy, only work under same origin
Regarding the width and height:
var img = document.getElementById('imageId');
var width = img.clientWidth;
var height = img.clientHeight;
Regarding the filesize you can use performance
var size = performance.getEntriesByName(url)[0];
console.log(size.transferSize); // or decodedBodySize might differ if compression is used on server side
Service workers have access to header informations, including the Content-Length header.
Service workers are a bit complicated to understand, so I've built a small library called sw-get-headers.
Than you need to:
subscribe to the library's response event
identify the image's url among all the network requests
here you go, you can read the Content-Length header!
Note that your website needs to be on HTTPS to use Service Workers, the browser needs to be compatible with Service Workers and the images must be on the same origin as your page.
Most folks have answered how a downloaded image's dimensions can be known so I'll just try to answer other part of the question - knowing downloaded image's file-size.
You can do this using resource timing api. Very specifically transferSize, encodedBodySize and decodedBodySize properties can be used for the purpose.
Check out my answer here for code snippet and more information if you seek : JavaScript - Get size in bytes from HTML img src
You can use generic Image object to load source dynamically then measure it:
const img = new Image();
img.src = this.getUrlSource()
img.onload = ({target}) =>{
let width = target.width;
let height = target.height;
}
You can get the dimensions using getElement(...).width and ...height.
Since JavaScript can't access anything on the local disk for security reasons, you can't examine local files. This is also true for files in the browser's cache.
You really need a server which can process AJAX requests. On that server, install a service that downloads the image and saves the data stream in a dummy output which just counts the bytes. Note that you can't always rely on the Content-length header field since the image data might be encoded. Otherwise, it would be enough to send a HTTP HEAD request.
You can find dimension of an image on the page using something like
document.getElementById('someImage').width
file size, however, you will have to use something server-side
var img = new Image();
img.src = sYourFilePath;
var iSize = img.fileSize;
The only thing you can do is to upload the image to a server and check the image size and dimension using some server side language like C#.
Edit:
Your need can't be done using javascript only.
The Problem: Imagine I have a td element structured as such:
<td td="" colspan="2">
<br>
<img src="ImageServlet" alt="random image" border="0" id="simpleRandomImg">
</td>
of course, with other HTML around it. The image contained within the servlet doesn't have a useful src indicated in the HTML (clearly) - however, when I open up the Network tab in Chrome, I can preview the loaded image itself and copy the image as a data URI - which gives me a stable URL like:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAAAyCAIAAACWMwO2AAAhQklEQVR42u2dB1Sb5/nonTRt2pPkJGmSNm180zhNTkabuPGKMeAwxBaIJYEYWmhLSEIggRhaaAIaIEASEggQew+zzPLEdjyy07T/9t9129t129u9z7mP9LnfXxZCBuLeS/71c77zIRQCGj89z+993+d7vS/GH7GxsXH+wGAwCf5ITExMSkpKTk5OSUlJTU1N80d6etcetcetc...
so, clearly there exists a stable reference to the loaded image. And clearly my browser 'knows' what that reference is, because I can retrieve the link to it as a data URI - but there's no reference to that data URI in the actual HTTP response. This probably seems a lot less mystical to someone who understands JavaScript, but that someone is not myself - so could someone explain what's going on here, and if there is some way to gather the data image URI from the HTTP response?
Attemped Solutions:
Did a little digging around in the HTTP response and located this bit of JavaScript which, apparently, handles the changing of images:
function changeImage() {
// makes a new image load
var obj=document.getElementById('simpleRandomImg');
if (obj != null) {
// append a unique index to force browser to reload
obj.src='ImageServlet?'+(cnt++);
}
However, nothing I see there gives any indication as to the actual URI location of the image. As before, if I open the Google Chrome network tab and attempt to retrieve the data image URI from the individual response, it works and gives me a valid URI - so, clearly the browser is receiving it. How can I access it?
e: to be clear, I do not control the website in question, so I can't 'fix' it by just changing the internal javascript - I'm viewing the site and am interested in whether or not it's possible to retrieve the loaded images short of screenshotting the page itself.
Something like this should work. Canvas API has a function called toDataURI
function getDataUri(url, callback) {
var image = new Image();
image.onload = function () {
var canvas = document.createElement('canvas');
canvas.width = this.naturalWidth; // or 'width' if you want a special/scaled size
canvas.height = this.naturalHeight; // or 'height' if you want a special/scaled size
canvas.getContext('2d').drawImage(this, 0, 0);
// Get raw image data
callback(canvas.toDataURL('image/jpg').replace(/^data:image\/(png|jpg);base64,/, ''));
// ... or get as Data URI
callback(canvas.toDataURL('image/jpg'));
};
image.src = url;
}
// Usage
// beware of server cors settings..
getDataUri('image url here', function(dataUri) {
console.log(dataUri)
// Do whatever you'd like with the Data URI!
});
You can set the src of the image to your URI by using setAttribute, like so:
obj.setAttribute('src', uriString)
I am using the img-Tag to display SVG images which my users uplaoded to my Amazon S3 bucket.
<img src="http://testbucket.s3.amazonaws.com/mysvg.svg" />
The problem is that, once the image is loaded and shown, it's not the size it was constructed for. For example a test image is displayed with a size of 1920x1920px but its ViewBox is 0 0 128 128. However, I would like to display the image in the size of its ViewBox. In this case 128x128px.
The only idea I came up with is downloading the image with an ajax request and parse the ViewBox attribute out of the svg source. Than I adjust the size. However, I do not want to make any unnecessary http requests and I am looking for a solution to read the images ViewBox dimensions. I could use the <object>-Tag to load the image but since it's hosted on Amazon I get a cross domain issue and cannot read the svgs source. Even when adding CORS rules to my bucket.
Ideas?
You could try this approach :
Download the svg file with an Ajax request.
Parse the viewbox of that svg from the response.
Create a new Image();
Set the image's src to "data:image/svg+xml; charset =utf8,"+encodeURIComponent(the ajax response).
Set the image width/height from the viewBox infos you grabbed step 2 (via css or in its attributes).
That should work for all majors browsers supporting svg in <img>, and since you just appended the response string into the image src, you only make one single request to Amazon's servers.
So, e.g, it could give you a function like so :
function viewBoxedImgSVG(container, url){
//There might be a better way to parse it...
var parseViewBox = function(data){
var parsed = data.match(/viewBox="(.*?)."/);
if(parsed) return parsed[1].split(' ');
}
var xhr= new XMLHttpRequest();
xhr.onload = function(){
var img = new Image();
// responseText for IE, but doesn't support SMIL in img tag anyway
img.src = "data:image/svg+xml; charset=utf8,"+encodeURIComponent(xhr.responseText);
var vB = parseViewBox(xhr.responseText);
if(vB){
img.width = vB[2];
img.height = vB[3];
}
container.appendChild(img);
}
xhr.open('GET', url);
xhr.send();
}
I have a couple of suggestions:
I am not sure if this works - in general, or with AWS - but you could try doing an AJAX request with "Range" HTTP request header. The idea being that you only fetch the first few hundred bytes of the SVG file.
Assuming you are in control of the uploading, you could add the dimensions to the file name. Ie. "mysvg_128x128.svg" and parse the values from the name.
I'd like to be able to get the file size on an image on a webpage.
So let's say I have an image on the page (that has loaded) like this:
How do I call a function in Javascript (or, even better, jquery) to get the file size (not the dimensions) of the image?
It's important to note that I'm not using any inputs or having users upload the image, there's lots of SO answers on getting image sizes from browse buttons with the file API.
All I want to do is get the file size of any arbitrary image on the page based of it's id and src url.
Edit: I'm dealing with a keep-alive connection for some images so the Content-Length headers are not available.
You can't directly get the file size (or any data from it).
The only way is a bit dirty, because you have to do a XMLHTTPRequest (and it probably won't work with externals images, according to the "Cross Origin Resource Sharing"). But with the browser's cache, it should not cause another HTTP request.
var xhr = new XMLHttpRequest();
xhr.open("GET", "foo.png", true);
xhr.responseType = "arraybuffer";
xhr.onreadystatechange = function() {
if(this.readyState == this.DONE) {
alert("Image size = " + this.response.byteLength + " bytes.");
}
};
xhr.send(null);
Here is a React version that worked for me, posting it in the event that it could save some time.
Form field
<input type="file"
name="file"
id="file"
multiple="multiple"
onChange={onChangeHandler}
className="fileUp" />
JSX
const onChangeHandler = event => {
console.log(event.target.files[0].size);
}
Old question but here is my take: I had the same issue but didn't want te rely on buffering to prevent my app from downloading the images twice (as the accepted answer does). What I ended up doing was fetch the file as a blob, read the file size, and use the blob as image source from there on. this was easily done with the URL.createObjectURL() function like so:
let img = new Image()
img.crossOrigin = "Anonymous"; // Helps with some cross-origin issues
fetch('foo.png')
.then(response => response.blob())
.then(blob => { img.src = URL.createObjectURL(blob); })
This looks a lot cleaner and perhaps it can still be useful for some.
Assuming you could use HTML5
Create a canvas
Put image in there
Grab the image data with .toDataURLHD()
Grab the base64 encoded string
Use some mad calculation to get the image size in bytes.
Have fun!
Maybe it's a lot easier to use a server side script, so it won't be HTML5-dependant