【问题标题】:How can i return the resized image from filereader如何从文件阅读器返回调整大小的图像
【发布时间】:2026-01-18 07:50:02
【问题描述】:

我创建了一个负责调整图像大小的函数,现在我想从函数中返回调整后的图像。

我正在与react 合作,我知道我可以使用state 解决问题,但我不喜欢这个解决方案..

尝试返回每个不同的范围,但我仍然收到 undefined 响应。此外,当我返回 reader 本身时,我会得到旧数据。

接口

   interface IHTMLInputEvent extends Event {
        target: HTMLInputElement & EventTarget;
    }

调整大小功能

function resizeImage(file: IHTMLInputEvent, width: number) {

        const fileName = file.target.files[0].name;
        const reader = new FileReader();

        reader.readAsDataURL(file.target.files[0]);

        reader.onload = (event) => {
            const img = new Image();
            img.src = event.target.result.toString();

            img.onload = () => {
                const elem = document.createElement('canvas');
                const scaleFactor = width / img.width;
                elem.width = width;
                elem.height = img.height * scaleFactor;

                const ctx = elem.getContext('2d');
                ctx.drawImage(img, 0, 0, width, img.height * scaleFactor);

                ctx.canvas.toBlob((blob) => {
                    return new File([blob], fileName, { type: 'image/jpeg', lastModified: Date.now() });
                }, 'image/jpeg', 1);
            };
        };
    }

处理上传的函数(到目前为止)

function handleUpload(e: IHTMLInputEvent) {
        const resizedImage = resizeImage(e, 600);
    }

输入框

 <input 
   className={classes.inputForUpload} 
   accept='image/*' 
   type='file' 
   ref={uploadImage} 
   onChange={(e: IHTMLInputEvent) => handleUpload(e)} 
  />

我想返回新创建的图像。

【问题讨论】:

    标签: javascript reactjs typescript canvas filereader


    【解决方案1】:

    你可以使用 Promise 解决这个问题,

    function resizeImage(file: IHTMLInputEvent, width: number) {
    
      return new Promise((resolve, reject) => {
    
        const fileName = file.target.files[0].name;
        const reader = new FileReader();
    
        reader.readAsDataURL(file.target.files[0]);
    
        reader.onload = (event) => {
          const img = new Image();
          img.src = event.target.result.toString();
    
          img.onload = () => {
            const elem = document.createElement('canvas');
            const scaleFactor = width / img.width;
            elem.width = width;
            elem.height = img.height * scaleFactor;
    
            const ctx = elem.getContext('2d');
            ctx.drawImage(img, 0, 0, width, img.height * scaleFactor);
    
            ctx.canvas.toBlob((blob) => {
              resolve(new File([blob], fileName, {
                type: 'image/jpeg',
                lastModified: Date.now()
              }));
            }, 'image/jpeg', 1);
          };
        };
      });
    }
    

    asyncawait

    async function handleUpload(e: IHTMLInputEvent) {
      const resizedImage = await resizeImage(e, 600);
    
      // do you suff here
    }
    

    JSX,

    <input 
       className={classes.inputForUpload} 
       accept='image/*' 
       type='file' 
       ref={uploadImage} 
       onChange={async (e: IHTMLInputEvent) => await handleUpload(e)} 
    />
    

    【讨论】:

    • 谢谢!我真的对此感到头疼,也许是时候让我深入研究异步等待了(我仅将其用于服务器响应等)。您能否澄清一下为什么需要异步等待?
    • 您可以使用.then(callback) 一种较旧的解决承诺的方法。 async await 是以同步方式编写异步功能的语法糖。好读:medium.com/javascript-in-plain-english/…
    【解决方案2】:

    请问您为什么不喜欢使用状态的解决方案?这似乎是一个非常标准的用例。

    您的状态可能如下所示:

    state = {
      imageDescription: '',
      imageUrl: null
    };
    

    您的操作处理程序将在成功时简单地设置状态,如下所示:

    img.onload = () => {
      ...
    
      this.setState({ imageDescription: fileName, imageSrc: img.src })
    };
    

    最后你的渲染函数看起来像这样:

    render() {
      const { imageDescription, imageUrl } = this.state;
    
      return (
        <Fragment>
           <input 
             className={classes.inputForUpload} 
             accept='image/*' 
             type='file' 
             ref={uploadImage} 
             onChange={(e: IHTMLInputEvent) => handleUpload(e)} 
           />
           <img src={imageUrl} alt={imageDescription} />
        </Fragment>
      )
    }
    

    附:你可以删除handleUpload,直接调用resizeImage。

    【讨论】:

    • 感谢您的回复!我不想使用state,因为我想保持这个组件简单和即插即用(在多个地方使用)。使用状态时,我需要在要使用此功能的每个组件中定义状态。在将图像发送到后端之前,我正在使用此功能调整图像大小
    • 您只需要在这个组件中定义状态,不一定是导入它的父组件。不过,我明白您对调整大小功能的看法——到目前为止,我在文件上传方面的经验总是需要重新渲染以加载缩略图或通知用户进度。