I have a simple web application to manipulate image files in the browser.
It is entirely client side. I had some questions about the safety of the operation here : Is this client side application secure?
I want to validate the files to make sure only allowed formats can be 'uploaded'. I put uploaded in quotations because, I'd like to repeat, everything happens on the client side in javascript.
PNG files specifically
I am learning about the structure of png files and I am thinking of using the fileReader object and the method readAsArrayBuffer() to read the bytes of the png file so that I can evaluate the first 8 bytes of the header (137 80 78 71 13 10 26 10) along with the chunk types (like IHDR, IDAT, IEND etc.) and the CRCs. In fact I have already done so but it isn't a part of my web app. Basically, when a user tries to 'upload' a file, my app would spot check some key bytes of the file and determine roughly 'OK, this is a png file. It's ok to work with this file'.
Would this be a good enough validation?
My reasoning for this precaution, even to this whole thing is client-side, is to protect an unsuspecting end user who might 'upload' a file that looks like a png, but which actually contains some harmful script with it.
If this isn't sufficient, I'm hoping someone can point me in such a direction so that I will know what does constitute a proper validation.
I had to check filetype in file uploader to determine if file was image (jpg, png) and I decided to do it by reading file's magic number (4 first bytes) with FileReader but I have some doubts about this method:
Is this method safe? Is there a way to upload non jpg file as jpg with this method?
I've seen filetypes with different magic numbers size like 2, 4, 6 bytes... So If I had to make a generic method to determine not just image filetype but the others as well, I would have to read the maximum amount of bytes (to determine largest magic number) from the file, right?
It's not safe. Problem is not only in magic numbers but already in that you try to validate it on client side.
Form can be uploaded directly from a script bypassing your client side validation.
Correct way to do it is to validate everything on server side using proven techniques.
Right. Different file formats have different magic numbers on different offsets. But still if you care about security - dont trust anything.
My program Precomp can be used to further compress already compressed file formats like GIF, PNG, PDF, ZIP and more. Roughly summarized, it does this by decompressing the compressed streams, recompressing them and storing the differences between the expected compressed stream and the actual compressed stream. As an example, this rotating earth picture from Wikipedia is compressed from 1429 KB to 755 KB. The process is lossless, so the original GIF file can be restored.
The algorithm for the GIF file format can be isolated and implemented relatively easy, so I was thinking about a proof-of-concept implementation in JavaScript. This would lead to the web server sending a compressed version of the GIF file (.pcf ending, essentially a bzip2 compressed file of the
GIF image contents) and the client decompressing the data, recompressing to GIF and displaying it. The following things would've to be done:
The web site author would've to compress his GIF files using the standard version of Precomp and serve these instead of the GIF files together with a JavaScript for the client side recompression.
The client would decompress the bzip2 compressed file, this could be done using one of the existing bzip2 Javascript implementations.
The client would recompress the image content into the original GIF file.
The process is trade of bandwidth against CPU usage on the client side.
Now my questions are the following:
Are there any general problems with the process of loading a different file and "converting" it to GIF?
What would you recommend to display before the client side finishes (image placeholder)?
What do I have to do to make sure the .pcf file is cached? Bandwidth savings were useless if doesn't get cached.
Is there a way to display the original GIF if JavaScript is deactivated, but avoid loading the GIF if JavaScript is activated?
Can I give the users a way to configure the behaviour? E.g. on mobile devices, some might avoid bandwidth, but others might want less CPU usage.
Would it be possible to display interlaced GIFs as supposed (going from a rough version to the final image)? This would require updating the image content multiple times at different stages of recompression.
Let's begin by answering your specific questions. Code example below.
Q&A
Are there any general problems with the process of loading a different file and "converting" it to GIF?
The main problem is complication. You are effectively writing a browser addon, like those for JPEG2000.
If you are writing real browser addons, each major browsers do it differently, and change addon formats occasionally, so you have to actively maintain them.
If you are writing a JS library, it will be easier to write and maintain, but it will be unprivileged and suffer from limitations such as cross original restriction.
What would you recommend to display before the client side finishes (image placeholder)?
Depends on what your format can offer.
If you encode the image dimension and a small thumbnail early, you can display an accurate place-holder pretty early.
It is your format, afterall.
What do I have to do to make sure the .pcf file is cached? Bandwidth savings were useless if doesn't get cached.
Nothing different from other files.
Configure the Expires and Cache-Control header on server side and they will be cached.
Manifest and prefetch can also be used.
Is there a way to display the original GIF if JavaScript is deactivated, but avoid loading the GIF if JavaScript is activated?
This is tricky. When JavaScript is disabled, you can only replace elements, not attributes.
This means you cannot create an image somewhere that points to the .pcf files, and ask browser to rewrite the src attribute when JS is unavailable.
I think the best solution to support no JS is outputting the images with document.write, using noscript as fall back:
<noscript>
<img src=demo.gif width=90>
</noscript><script>
loadPcf("demo.pcf","width=90")
</script>
(Some library or framework may make you consider <img src=demo.gif data-pcf=demo.pcf>.
This will not work for you, because browsers will preload 'demo.gif' before your script kicks in, causing additional data transfer.)
Alternatively, browser addons are unaffected by "disable JS" settings, so if you make addons instead then you don't need to worry about it.
Can I give the users a way to configure the behaviour? E.g. on mobile devices, some might avoid bandwidth, but others might want less CPU usage.
Perhaps. You can code a user interface and store the preference in cookie or in localStorage.
Then you can detect preference and switch the logic in server code (if cookie) or in client code.
If you are doing addons, all browsers provide reliable preference mechanism.
The problem is that, again, every browser do it differently.
Would it be possible to display interlaced GIFs as supposed (going from a rough version to the final image)? This would require updating the image content multiple times at different stages of recompression.
If you hands browsers a partial image, they may think the image is corrupted and refuse to show it.
In this case you have to implement your own GIF decoder AND encoder so that you can hands browser a complete placeholder image, just to be safe.
(new) Can I decode image loaded from another site?
I must also repeat the warning that non-addon JS image decoding does not work with cross origin images.
This means, all .pcf files must be on the same server, same port, and same protocol with the site using it.
For example you cannot share images for multiple sites or do optimisations like domain sharding.
Code Example
Here is a minimal example that creates an <img>, loads a gif, half its width, and put it back to the <img>.
To support placeholder or progressive loading, listen to onprogress instead of/in addition to onload.
<!DOCTYPE html><html><head><meta charset="UTF-8"><script>
function loadPcf( file, attr ) {
var atr = attr || '', id = loadPcf.autoid = 1 + ~~loadPcf.autoid;
document.write( '<img id=pcf'+id+' ' + atr + ' />' );
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer'; // IE 10+ only, sorry.
xhr.onload = function() { // I am loading gif for demo, you can load anything.
var data = xhr.response, img = document.querySelector( '#pcf' + id );
if ( ! img || ! data instanceof ArrayBuffer ) return;
var buf = new DataView( data ), head = buf.getUint32(0), width = buf.getUint16(6,1);
if ( head !== 1195984440 ) return console.log( 'Not a GIF: ' + file ); // 'GIF8' = 1195984440
// Modify data, image width in this case, and push it to the <img> as gif.
buf.setInt16( 6, ~~(width/2), 1 );
img.src = URL.createObjectURL( new Blob( [ buf.buffer ], { type: "image/gif" } ) );
};
xhr.open( 'GET', file );
xhr.send();
}
</script>
<h1>Foo<noscript><img src=a.gif width=90></noscript><script>loadPcf("a.gif","width=90")</script>Bar</h1>
If you don't need <noscript> compatibility (and thus prevent facebook/google+ from seeing the images when the page is shared), you can put the pcf file in <img> src and use JS to handle them en mass, so that you don't need to call loadPcf for each image and will make the html much simpler.
How about <video>?
What you envisioned is mostly doable, in theory, but perhaps you should reconsider.
Judging from the questions you ask, it will be quite difficult for you to define and pull off your vision smoothly.
It is perhaps better to encode your animation in WebM and use <video> instead.
Better browser support back to IE 9 - just add H.264 to make it a dual format video. You need IE 10+ to modify binary data.
Size: Movies has many, many options and tricks to minimise size, and what you learned can be reused in the future.
Progressive: <video> have had some techs for adaptive video, and hopefully they will stabilise soon.
JavaScript: <video> does not depend on JavaScript.
Future-proof: Both WebM and H.264 will be supported by many programs, long after you stopped working on your special format.
Cost-effective: Create a low-bandwith option using smaller or lower quality media is easier and more reliable than a custom format. This is why wikipedia and youtube offers their media in different resolutions.
For non-animations, PNG can also be colour indexed and 7z optimised (keeping the PNG format).
Icon size indexed PNG is often smaller than the same GIF.
Or perhaps your vision (as described in the pcf website) is the capability to encode many different files, not only GIF.
This will be more like supporting a new network protocol, and is likely beyond the scope of humble JavaScript. (e.g. how are you going to handle pdf download or streaming?)
I make multiple file uploading project, (Server language is PHP)
Especially, I need that before uploading, on client side, get files size in bytes.
what is today best cross browser solution/plugin for this?
I find SWFUpload, may be exists better solution? or use this SWFUpload?
In HTML5 a file drag & drop event creates a File object which has a .size property.
Look in ev.dataTransfer for the list of File objects.
Is there a library like canvas2image (please look at createBMP function) for making .tiff in JavaScript (browser or nodejs)?
Native browser-support for tiff files is still pretty bad. Wikipedia has a nice overview regarding browser Image format support.
That being said; since a .tiff image is still essentially a raster-image, one could indeed convert it (the tricky part is stuff like supporting different compression-algorithms like PACKBITS, DEFLATE, LZW, etc) to another (browser-supported) raster-format (so one could feed it as a data:img-source).
There is a library called Tiffus: a client side pure Javascript imaging library to load, save and manipulate binary images.
The original project aim was was to create a plain Javascript chrome extension which can convert single/multi page TIFF image/s to BMP/GIF image (this is where the name came from).
However now it supports:
Windows BMP (no compression, RLE)
OS/2 BMP
ICO
GIF
JPEG
PNG
TIFF
and currently supports the folloing image functions:
load
save
resize
flip
invert color
Basically it works like this:
Source image downloaded as Binary Data using XMLHttpRequest with
MimeType('text/plain; charset=x-user-defined'); (future: HTML5
Canvas ImageData)
Imageprocessing using Tiffus
Destination image shown as Data URI scheme (future: HTML5 Canvas ImageData)
Note that according to the above; the author expects to use HTML5 Canvas ImageData in the future.
Hope this helps!