简体   繁体   中英

How to get image width and height in a web-worker?

Here's my code:

// process-image.js (web-worker)
self.addEventListener('message', ev => {
    const {id,file} = ev.data;

    const reader = new FileReader();
    reader.onload = ev => {
        const imageFile = new Image(); // <-- error is here
        imageFile.src = ev.target.result;

        imageFile.onload = () => {
            const fit = makeFit(imageFile);

            self.postMessage({
                id,
                file: {
                    src: ev.target.result,
                    width: fit.width,
                    height: fit.height,
                }
            })
        }
    };

    reader.readAsDataURL(file);
});

This was working fine in the main UI thread, but apparently I don't have access to Image inside of a web-worker. The specific error is:

Uncaught ReferenceError: Image is not defined at FileReader.reader.onload (process-image.js:12)

Is there another way get the width and height of an image?

I'd like to support as many file types as possible, but just JPG is good enough for now if there's some sort of library that can do this and will run in a web-worker.


Here's the relevant bit from the UI thread:

// some-react-component.js
    componentDidMount() {
        this.worker = new ImageWorker();
        this.worker.addEventListener('message', ev => {
            const {id, file} = ev.data;

            this.setState(({files}) => {
                files = files.update(id, img => ({
                    ...img,
                    ...file,
                }))

                const withWidths = files.filter(f => f.width);
                const avgImgWidth = withWidths.reduce((acc, img) => acc + img.width, 0) / withWidths.size;

                return {files, avgImgWidth};
            });
        });
    }


    onDrop = ev => {
        ev.preventDefault();


        Array.prototype.forEach.call(ev.dataTransfer.files, file => {

            const id = shortid();
            this.worker.postMessage({id, file});

            const img = {
                id,
                width: null,
                height: null,
                src: null,
                name: file.name,
            }
            this.setState(({files}) => ({files: files.set(id, img)}));
        });
    }

Only thing worth noting here is that id is just a random UUID, and file is a File . I'm passing the whole thing to the web-worker so that I can do all the processing there.

I think there might be a simpler solution:

https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap

I managed to get the size without using FileReader at all:

http://jsfiddle.net/hL1Ljkmv/2/

var response = `self.addEventListener('message', async ev => {
    const {id,file} = ev.data;
    console.log('received data');
    let image = await self.createImageBitmap(file);
    console.log(image);
});`;

const blob = new Blob([response], {type: 'application/javascript'});
const worker = new Worker(URL.createObjectURL(blob));

const input = document.querySelector('#file-input');
input.addEventListener('change', (e) => {
  worker.postMessage({file: input.files[0], id: 1})
});

I managed to get the width and height of most JPEGs by following this spec .

self.addEventListener('message', ev => {
    const {id, file} = ev.data;

    const reader = new FileReader();
    reader.onload = ev => {
        const view = new DataView(reader.result);

        let offset = 0;
        let header = view.getUint16(offset, false);

        if(header !== 0xFFD8) {
            throw new Error(`Not a JPEG`);
        }
        offset += 2;

        for(; ;) {

            header = view.getUint16(offset, false);
            offset += 2;
            let length = view.getUint16(offset, false);

            if(header === 0xFFC0) break;
            offset += length;
        }

        const height = view.getUint16(offset + 3);
        const width = view.getUint16(offset + 5);

        const fit = makeFit({width, height});

        self.postMessage({
            id,
            file: {
                src: URL.createObjectURL(file),
                width: fit.width,
                height: fit.height,
            }
        })
    };

    reader.readAsArrayBuffer(file);
});

This code is flakey as heck, but surprisingly it works. I'm still looking for a more robust solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM