如何在网络工作者中获取图像的宽度和高度?

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

这是我的代码:

// 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);
});

这在 UI 主线程中运行良好,但显然我无法访问网络工作者内部的 Image。具体错误为:

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

是否有其他方法获取图像的宽度和高度?

我想支持尽可能多的文件类型,但如果有某种类型的库可以做到这一点并且将 运行 在网络工作者中使用,那么现在只需要 JPG 就足够了。


这是来自 UI 话题的相关内容:

// 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)}));
        });
    }

这里唯一值得注意的是id只是一个随机的UUID,而file是一个File。我正在将整个事情传递给网络工作者,以便我可以在那里进行所有处理。

我按照 this spec.

设法获得了大多数 JPEG 的宽度和高度
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);
});

这段代码很古怪,但令人惊讶的是它有效。我仍在寻找更强大的解决方案。

我认为可能有更简单的解决方案:

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

我根本不使用 FileReader 就设法获得了大小:

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})
});