Reliable way to check image URL with JavaScript - javascript

In my web app, I'm having bunch of URLs types by users. My view is showing either link icon or image preview, depending on whether URL points to an image or not.
What is the most reliable way to display image previews only if it's really an image? Should I create <img> and check dimensions on load, use new Image() object, or maybe something else?
Any advice highly appreciated! :)

The most reliable and compatible way to check if the source point is a valid image is to use the Image object. If it can load it as an image onload will trigger, if not onerror will trigger:
var img = new Image;
img.onload = function() { /* OK */ };
img.onerror = function() { /* cannot read this as an image */ };
img.onabort = function() { /* connection or source was reset */ };
img.src = "URLtoCheckHere";
The drawback is of course that the whole image would be loaded before you could find out if the browser can read and decode it.
If Image for some reason can't be used you could read the source point via XHR but that comes with its own restrictions, one being cross-origin resource sharing. If the image is not of the same origin (protocol, domain, port etc.) and the external server does not allow cors-usage, reading would simply fail.
However, if these limitations aren't a problem (i.e. the images comes from the same server as the page or a cors-friendly site) then you could read in part of the image as ArrayBuffer and check the magic numbers for the image types you want to support. Be aware of byte-order.
To specify a range use the Range header (it's not sure the server will respect it though).
Assuming the part of the file has been read into an ArrayBuffer:
var reader = new DataView(arrBuffer);
if (reader.getUint32(0) === 0x89504E47 &&
reader.getUint32(4) === 0x0D0A1A0A) {
// this seem to be a PNG file
}
else ... etc.
But, even if the file is detected as a valid image file, there is no guarantee of that the file is not corrupt and so forth. There is no way to validate the file using this method (unless you chose to write a parser yourselves).
So in conclusion, Image is the best option in most scenarios. It has been through several iterations over the years in the main browsers and is pretty robust and stable.

Related

Is it possible to detect file extension change with Javascript during HTML upload

I have a hypothetical question. I currently am using AngularJS in my application and I am using a third party module, ng-file-upload, for file uploads. Now obviously I can check the file extension of the uploaded file and exclude it / prevent it from being sent to the server should it be undesirable, for example I only wish to allow the upload of images and the user uploads a word document. However I was thinking. Should a malicious user change a file from say "nastyfile.exe" to "nastyfile.gif" the malicious file would pass my check/validation using the File.type property as the File.type would be image/gif. To my knowledge there is no way on the frontend I could check if the original file extension has been modified using JavaScript. Is this the case or is there a way to determine this?
Thanks in advance.
Try to create an image.
Then listen error & load event if load trigger, it is an image, else if error trigger it is not an image ;)
var img = new Image();
img.onerror = function() { /* not an image */ }
img.onload = function() { /* it is an image */ }
img.src = 'PATH/FILENAME';

Force image caching with javascript

I am trying to clone an image which is generated randomly.
Although I am using the exact same url a different image is load. (tested in chrome and firefox)
I can't change the image server so I am looking for a pure javascript/jQuery solution.
How do you force the browser to reuse the first image?
Firefox:
Chrome:
Try it yourself (maybe you have to reload it several times to see it)
Code:
http://jsfiddle.net/TRUbK/
$("<img/>").attr('src', img_src)
$("<div/>").css('background', background)
$("#source").clone()
Demo:
http://jsfiddle.net/TRUbK/embedded/result/
You can't change the image server if it isn't yours, but you can trivially write something on your own server to handle it for you.
First write something in your server-side language of choice (PHP, ASP.NET, whatever) that:
Hits http://a.random-image.net/handler.aspx?username=chaosdragon&randomizername=goat&random=292.3402&fromrandomrandomizer=yes and downloads it. You generate a key in one of two way. Either get a hash of the whole thing (MD5 should be fine, it's not a security-related use so worries that it's too weak these days don't apply). Or get the size of the image - the latter could have a few duplicates, but is faster to produce.
If the image isn't already stored, save it in a location using that key as part of its filename, and the content-type as another part (in case there's a mixture of JPEGs and PNGs)
Respond with an XML or JSON response with the URI for the next stage.
In your client side-code, you hit that URI through XmlHttpRequest to obtain the URI to use with your images. If you want a new random one, hit that first URI again, if you want the same image for two or more places, use the same result.
That URI hits something like http://yourserver/storedRandImage?id=XXX where XXX is the key (hash or size as decided above). The handler for that looks up the stored copies of the images, and sends the file down the response stream, with the correct content-type.
This is all really easy technically, but the possible issue is a legal one, since you're storing copies of the images on another server, you may no longer be within the terms of your agreement with the service sending the random images.
You can try saving the base64 representation of the image.
Load the image in an hidden div/canvas, then convert it in base64. (I'm not sure if a canvas can be hidden, nor if it is possible to convery the img using html4 tag)
Now you can store the "stringified" image in a cookie, and use it unlimited times...
The headers being sent from your random image generator script include a Cache-Control: max-age=0 declaration which is in essence telling the browser not to cache the image.
You need to modify your image generator script/server to send proper caching headers if you want the result to be cached.
You also need to make sure that the URL stays the same (I didn't look at that aspect since there were tons of parameter being passed).
There seems to be two workarounds:
If you go with the Canvas method, see if you can get the image to load onto the Canvas itself so that you can manipulate the image data directly instead of making a 2nd http request for the image. You can feed the image data directly onto a 2nd Canvas.
If you're going to build a proxy, you can have the proxy remove the No-Cache directive so that subsequent requests by your browser use the cache (no guarantees here - depends on browser/user settings).
First off, you can "force" anything on the web. If you need to force things, then web development is the wrong medium for you.
What you could try, is to use a canvas element to copy the image. See https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Using_images for examples.
Tell it to stop getting a random image, seems to work the way you want when I add this third replace call:
// Get the canvas element.
var background = ($("#test").css('background-image')),
img_src = background.replace(/^.+\('?"?/, '').replace(/'?"?\).*$/, '').replace(/&fromrandomrandomizer=yes/,'')
try:
var myImg = new Image();
myImg.src = img_src;
and then append "myImg" to where you want:
$(document).append(myImg);
I did this with your fiddler scripts and got the same image every time
#test {
background:url(http://a.random-image.net.nyud.net/handler.aspx?username=chaosdragon&randomizername=goat&random=292.3402&fromrandomrandomizer=yes);
width: 150px;
height: 150px;
}
note the .nyud.net after the domain name.

Is there a way to let a user view an image they are about to upload client side before uploading to the server?

When a user is uploading an image, is there a way I can load the image client side and show it to them first, before uploading it to the server? Preferably using javascript/jquery only, but using flash would be acceptable too.
It is possible with the new FileReader interface defined in HTML5 and works on Firefox currently.
A file input has an associated files property which tracks the list of files currently selected for that input. To display a file from this list, create a new FileReader object, initialize its onload event handler, and read the file as a data URL.
// get the first file, foo is a file input field
var file = document.getElementById('foo').files[0];
// setup the reader and the load complete callback
var reader = new FileReader();
reader.onload = function(e) {
var image = new Image();
// string representing the image
image.src = e.target.result;
document.body.appendChild(image);
};
// read the file as a data url
reader.readAsDataURL(file);
Once the file is loaded, you will have access to its contents in a data url scheme, for instance:
data:image/jpeg;base64,...aqHI7sNyPGFjdtQvFr/2Q==
Create a new Image and set its src attribute to this data string.
See a working example here. Firefox only.
You can't really do this cross-browser in JavaScript alone due security restrictions that are in place, there are a few flash versions available though, here's one example (the free version does what you're after).
There are probably more free flash versions out there as well.
Since HTML 5 those things are possible, thanks to the File Object, File Reader and the ´files´ property of the input element.
See here for more information: http://demos.hacks.mozilla.org/openweb/ & http://hacks.mozilla.org/2009/12/file-drag-and-drop-in-firefox-3-6/.
Example (only for demonstration, requires FF 3.5+):
See here: http://gist.github.com/536024
In case you wonder, File.url is brand new, with it you dont anymore need to read the whole file into the memory, and assign the whole DataUrl (data:image/src,base64;DF15EDFE86..) to the src property.
Well, the <img> tag needs a path to the image. That path can be to something on the web, or to a local file. So far, so good. The trick is, how do you tell your javascript the path on the local system, so it can set the IMG SRC attribute.
The path of the file <input> tag is unavailable to javascript (as a security precaution --- you don't want a want page upload files from you system behind your back).
On the other hand, if you can get your users to enter a correct file path name into a text <input> field, then it should be possible.

Embed external images for use in HTML canvas?

I'm using JavaScript to load an image into my Canvas element in Firefox. This works fine for local images, but throws a security exception for external images. Is there any way to avoid this security exception, one that does not involve my server having to act as proxy to load the image locally (because that would stress my server)?
PS: The current code is similar to this:
var img = new Image();
var contextSource = canvasSource.getContext('2d');
contextSource.drawImage(img, 0, 0);
// get image data to do stuff with pixels
var imageDataSource = contextSource.getImageData(0, 0, width - 1, height - 1);
Actually it might be as well possible by utilizing XHR level 2 and UrlData. Check out my blog post.
This is doable for images under 25.6k in size. Use YQL's data:uri converter to grab the image and hand you back the base-64 encoded block. Then create an image in the client and set its source to the data you received from YQL. Example here:
http://kentbrewster.com/avatar-portraits
Here's a link to the right YQL data table.
[edited to fix; it's 25.6k, not 256]
I don't think this is possible, because this could open the browser to cross domain attacks.

How can you determine the file size in JavaScript?

I help moderate a forum online, and on this forum we restrict the size of signatures. At the moment we test this via a simple Greasemonkey script I wrote; we wrap all signatures with a <div>, the script looks for them, and then measures the div's height and width.
All the script does right now is make sure the signature resides in a particular height/width. I would like to start measuring the file size of the images inside of a signature automatically so that the script can automatically flag users who are including huge images in their signature. However, I can't seem to find a way to measure the size of images loaded on the page. I've searched and found a property special to IE (element.fileSize) but I obviously can't use that in my Greasemonkey script.
Is there a way to find out the file size of an image in Firefox via JavaScript?
Edit: People are misinterpreting the problem. The forums themselves do not host images; we host the BBCode that people enter as their signature. So, for example, people enter this:
This is my signature, check out my [url=http://google.com]awesome website[/url]!
This image is cool! [img]http://image.gif[/img]
I want to be able to check on these images via Greasemonkey. I could write a batch script to scan all of these instead, but I'm just wondering if there's a way to augment my current script.
As you know IE supports the fileSize property of an image. No such luck in other browsers ... however, you should be able to modify this script:
http://natbat.net/2008/Aug/27/addSizes/
It uses JSON to read HTTP headers of files and display their actual file size. That should help you prevent people uploading large animated GIFs.
As for getting the dimensions:
var img = new Image();
theImage.src = "someimage.jpg";
actualwidth = theImage.width;
actualheight = theImage.height;
This of course is a pure client-side approach to something best handled server-side.
Actually, with HTML5, this is now possible,
read more information here.
Short answer, you cannot
Also, Check on jGuru How can you check the file size from JavaScript in a form with an input type of file?
You will find some important points
Well the answer is very simple, you cannot.
Reason: The browser security does not allow the scripts
(Javascript/VBScript) or even applets and ActiveX Controls to read
files from the local hard disk. You can only read files if your code
is signed by some Certificate Authority (CA). Now the input type
"FILE" also does not have the permission to read files. We cannot do
anything about that since thats what HTML says. So since it cannot
read files, it cannot find the size of the file with which the input
tag is associated. Since it cannot find the file size, there is no
function exposed by JavaScript/VBScript to return the file size. But
if you need to find the file size, say in order to restrict the size
of the file uploaded to your web-server. Then you can do so by
counting the file contents on the server-side, once the user submits
it to the server. Thats what many of the free e-mail providers like
www.hotmail.com do.
Server side validation is always a better bet, but in your case, I can see why you would want to do this client side.
Also, it seems that others may have misread the question, and that the images that Daniel want to test are already uploaded, in which case there is a fairly simple method of doing so (provided the images are on the same domain as the script).
var getFileSize = function(address, responseHandler) {
var req = new XMLHttpRequest();
req.open('head', address, true);
req.onreadystatechange = responseHandler;
req.send(null);
}
var responseHandler = function(resp) {
if ( this.readyState == 1 ) {
this.abort();
}
console.log(this.getResponseHeader("Content-length"));
};
getFileSize("http://stackoverflow.com/content/img/so/logo.png", responseHandler);
Boom. This example works in FF3 and probably 2. Since you're using Greasemonkey to do this, browser compatibility doesn't seem like an issue.
I'm not certain if Greasemonkey shares the same XML RPC domain restrictions, but if the images files that you need are on a different domain than the script, then you might need to look into using some iframe magic.
Client side validation is insufficient to accomplish your goal. A simple post request will allow the user to upload any image they want no matter what html or javascript you serve them.
You could set a maximum file size in your HTML where they upload files.
<input type="hidden" name="MAX_FILE_SIZE" value="10000000">
(max_file_size in bytes).
However, this is an "undocumented/unsupported" item of some browsers. You are best to actually check the filesize on the server once it's been uploaded.
You could also use a Flash or Java applet to handle the upload and check the filesize there. See http://www.masrizal.com/product/custom%20tag/cf_flashmultiupload/docs%20&%20examples/example.cfm and http://www.saschawenning.de/labor/flash8/fileUpload/ for examples.
The DOM attribute img.fileSize will return the actual file size of the referenced <img>. Access to the img object can be obtained using JQuery or the DOM 'images' collection. However, this is an IE only extension.
Another approach is to omit the height and width attributes in the <img> tag, so that the full image is downloaded, then use img.height and img.width to determine the size of the downloaded image. This code could be put into the user's profile editor page as an intermediate step between having someone enter their signature as HTML, then showing them a preview of their signature. Clunky, I have to admit, but possible.
If you are worried about huge images, set a max upload size as Richy C. mentioned, but also resize the uploaded image on the server and use the resized version.
Facebook does this for most of the uploaded images so that reasonably size images are served. Even converting them to png format in some (most?) cases, which drive the creative group nuts because of "lost quality".
What you should be able to do is an AJAX HEAD request of the image url, which just gets the header of the file rather than the contents, so is much faster. One of the headers you will get back is Content-Length, and that will tell you the size of the image in bytes.
More details here.
You can do that file HTML5 JS File API
You can test run the below codes in my web IDE (but please use google chrome or FF):
http://codesocialist.com/#/?s=bN
The below codes retrieve the filetype and filesize for you :)
<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
// files is a FileList of File objects. List some properties.
var output = [];
for (var i = 0, f; f = files[i]; i++) {
output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ', f.size, ' bytes </strong></li>');
}
document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
}
// Bind Event Listener
document.getElementById('files').addEventListener('change', handleFileSelect, false);
} else {
alert('The File APIs are not fully supported in this browser.');
}

Categories