【问题标题】:How to get image width and height in a web-worker?如何在网络工作者中获取图像宽度和高度?
【发布时间】:2017-09-26 16:35:17
【问题描述】:

这是我的代码:

// 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。具体错误是:

未捕获的引用错误:图像未定义 在 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。我将整个事情传递给网络工作者,以便我可以在那里进行所有处理。

【问题讨论】:

    标签: javascript web-worker


    【解决方案1】:

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

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

    【讨论】:

    • 哇是的! createImageBitmap 看起来正是我想要的。甚至可能有助于调整大小,这是我的下一个任务。很快就会试试这个,谢谢!
    • 是的,对不起,复制粘贴很快就可以发布答案,将编辑,谢谢!
    • 您好,您成功调整createImageBitmap生成的图片大小了吗?
    • 不支持 Safari :(
    • URL.createObjectURL 似乎不适用于 Chrome 扩展服务工作者。
    【解决方案2】:

    通过关注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);
    });
    

    这段代码非常糟糕,但令人惊讶的是它可以工作。我仍在寻找更强大的解决方案。

    【讨论】:

    • createImageBitmap 肯定更容易,并且将支持浏览器支持的所有支持的图像类型,但如果您还需要支持旧浏览器,我确实写了something here,您可以从中提取代码其他格式。
    猜你喜欢
    • 1970-01-01
    • 2010-11-18
    • 1970-01-01
    • 1970-01-01
    • 2011-05-31
    • 1970-01-01
    • 1970-01-01
    • 2017-05-23
    • 2011-11-26
    相关资源
    最近更新 更多