File inputs
You can accept files with an input like so:
<input type="file" id="myfiles" name="files" accept="image/*" multiple="multiple"/>
If you want an image from the camera:
<input type="file" id="myfiles" name="files" accept="image/*" capture="environment" multiple="multiple"/>
Selecting files triggers a “change
” event.
To get the files in an event handler, get the element (either as a target of the event or with a selector) and access the “files
” property:
document.querySelector("#myfiles").files
This returns a FileList
, which you can use like an ECMAScript Array.
Each element of the FileList
is a File
object, which is a type of Blob
.
File to HTMLImageElement
Once you have a file, like
const myfile = document.querySelector("#myfiles").files[0];
You can use it as an HTMLImageElement
by setting the src
to a Blob URL like so:
const img1 = document.createElement("img");
img1.src = URL.createObjectURL(myfile);
Getting Image Dimensions
With the previous img1
element use img1.naturalWidth
and img1.naturalHeight
to get dimensions.
If you use img1.width
or img1.height
, you’ll get the rendered height, which can differ if you set the CSS “max-width: 100%
” (for example).
Also note that you can’t immediately get “naturalWidth
” after creating an “img
” and setting the “.src
”: it’ll initially return “0”.
You need to check after the “load
” event to read “naturalWidth
” and “naturalHeight
”.
HTMLImageElement to Canvas
To take that img1
and draw it on a canvas:
const canvas1 = document.createElement("canvas");
document.body.appendChild(canvas1);
canvas1.width = img1.naturalWidth;
canvas1.height = img1.naturalHeight;
const ctx1 = canvas1.getContext("2d");
ctx1.drawImage(img1, 0, 0);
Note that you’ll probably want to scale it down, as canvas elements have max dimensions.
Downloading a Canvas element
To save a canvas, I’ll get a Blob from the canvas, then make a link where
href
points to the blobdownload
is the name of the file to download
Canvas to Blob
Here’s how to get a WebP blob of the canvas:
const blob1 = await new Promise(resolve => { canvas1.toBlob(resolve, "image/webp", 0.80) });
where 0.80
is the quality of the WebP.
If you have an OffscreenCanvas
instead, use “.convertToBlob()
” like so:
const blob1 = await canvas1.convertToBlob({type: "image/webp", quality: 0.80});
Downloading the blob
Here’s how to make a link to download that blob:
const a1 = document.createElement("a");
a1.href = URL.createObjectURL(blob1);
a1.download = "example.webp";
a1.textContent = "Download the blob";
document.body.appendChild(a1);
Note that you can actually just click it programmatically to trigger a download like so:
a1.click();
Then you’ll probably want to clean up that element:
URL.revokeObjectURL(a1.href);
a1.remove();
You may actually be able to release the blob URL earlier according to an MDN article on Using files from web applications.
SVG to Blob
Like, I originally wanted to be able to rasterize an SVG to a canvas element.
While you can use an SVG-in-an-image-element and get that as a blob, I think you probably can’t directly rasterize an SVG-element-embedded-in-HTML.
This is prevented, I believe, for security reasons.
While SVG in <img>
elements can’t access network external resources, embedded SVGs can.
Like, an SVG could have a <foreignObject>
with an <iframe>
pointing to some cross-origin resource.
Being able to rasterize that would be a security flaw.
Probably your best bet would be to convert as much as you can to data URIs and have that used as an image element, and try to rasterize that.
SVG to File
To convert an SVG element to a File, we’ll serialize the XML as a string, then encode the string to a Uint8Array which we can use to construct a File.
Assume you have an SVG element named “svgel
”:
function svgToFile(svgel, filename) {
let str = new XMLSerializer().serializeToString(svgel);
let uintarray = new TextEncoder().encode(str);
return new File([uintarray], filename, { type: "image/svg+xml" });
}
You can then use this File to create an HTMLImageElement
as described previously.
Uploading blobs
If you wanted to modify the original <input type="file"> element, you’ll need to set its “files
” property.
However, you can’t directly modify a FileList
object, nor does FileList
appear to have a constructor.
Instead, we’ll make a DataTransfer
object and convert our Blob
to a File
, then we’ll set the input
’s “files
” property using our DataTransfer
object’s “files
” property.
Blobs to Files
Converting a Blob
to a File
is fairly straightforward.
You just need to provide a file name and optionally a media (MIME) type:
const f2 = new File([blob1], "somefilename.webp", { type: "image/webp" });
Modifying <input type="file">
Now that we have a file, we’ll make a DataTransfer
object and use that to set the <input type="file">
’s “files
” property:
const dt1 = new DataTransfer();
dt1.items.add(f2);
document.querySelector("#myfiles").files = dt1.files;
These tools should let you take in an image and modify it before uploading. Some reasons to do this would be to resize, compress, or strip EXIF metadata before uploading.