You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React文件上传:XMLHttpRequest抛出的错误无法被调用函数捕获的问题

问题描述

我正在ReactJS中开发一个允许用户向图片应用上传文件的功能。后端会验证文件类型,确保其为支持的格式(而非.txt、.bat等非图片格式)。若用户上传不支持的类型,服务器会返回HTTP 415错误「Unsupported Media Type」。我能在发送请求的sendCSRFRequest函数中获取该状态码,代码如下:

export function sendCSRFRequest(formData, targetURL) {  
    const request = new XMLHttpRequest();

    // check for HTTP response status
    request.onreadystatechange = () => {
        if (request.readyState === 4) {
            if (request.status === 200) {
                console.log("Upload successful!");
            } else if (request.status === 415) {
                throw new Error("Can't upload that!");
            }
        }
    };

    request.open('POST', targetURL);
    request.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
    request.send(formData);
};

这段代码能成功识别415错误并抛出「Can't upload that!」的错误信息,但我希望将该错误传递给调用函数,以便进行条件渲染并在UI中向用户提示错误。调用函数代码如下:

async function handleUpload(e, files, setFiles, setUploadFailed) {
    e.preventDefault() // do not redirect page
    const formData = new FormData();
    files.forEach(file => {formData.append('file', file)});
    try {
        sendCSRFRequest(formData, 'api/upload/'); // Function shown above
    } catch (e) {
        console.log("Found an error!");
        console.error(e);
        setUploadFailed(true); // Triggers an alert in the UI
    }
    
    await setFiles([]); // If upload is successful, clear out state "files"
};

但第二个函数无法捕获第一个函数抛出的错误,我尝试了多种写法(调整try/catch、添加/移除await、改用catch链式调用等)都无效。请问为何sendCSRFRequest中抛出的错误无法冒泡到调用函数?需要如何修改才能让错误被调用函数捕获?

原因分析
  • 错误抛出在onreadystatechange回调函数里,这个回调是异步执行的,属于XMLHttpRequest的异步事件回调,它的执行上下文和调用sendCSRFRequest的上下文完全分离。
  • 同步的try/catch只能捕获同一执行栈里的同步错误,无法捕获异步回调中抛出的错误,所以handleUpload里的try/catch根本抓不到这个错误。
修改方案

把sendCSRFRequest改造成返回Promise的函数,利用Promise的异步错误处理机制来传递错误:

第一步:重构sendCSRFRequest为Promise风格

export function sendCSRFRequest(formData, targetURL) {  
    return new Promise((resolve, reject) => {
        const request = new XMLHttpRequest();

        request.onreadystatechange = () => {
            if (request.readyState === 4) {
                if (request.status === 200) {
                    console.log("Upload successful!");
                    resolve(); // 请求成功时resolve
                } else if (request.status === 415) {
                    reject(new Error("Can't upload that!")); // 415错误时reject
                } else {
                    // 处理其他HTTP错误状态
                    reject(new Error(`Request failed with status ${request.status}`));
                }
            }
        };

        // 处理网络层面的错误(比如连接失败)
        request.onerror = () => {
            reject(new Error("Network error occurred"));
        };

        request.open('POST', targetURL);
        request.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
        request.send(formData);
    });
};

第二步:修改handleUpload函数,使用await配合try/catch

async function handleUpload(e, files, setFiles, setUploadFailed) {
    e.preventDefault();
    const formData = new FormData();
    files.forEach(file => {formData.append('file', file)});
    try {
        await sendCSRFRequest(formData, 'api/upload/'); // 用await等待Promise完成
        await setFiles([]); // 仅上传成功时清空文件列表
    } catch (e) {
        console.log("Found an error!");
        console.error(e);
        setUploadFailed(true);
    }
};

关键说明

  • 用Promise包裹XMLHttpRequest的异步逻辑,成功时调用resolve,失败(包括415错误、网络错误)时调用reject,让异步错误能通过await+try/catch捕获。
  • 将清空文件列表的逻辑移到try块内,避免上传失败后误清空用户选择的文件。
  • 新增request.onerror处理网络异常,覆盖更多错误场景。

内容的提问来源于stack exchange,提问作者cdyswnnr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 12:45:11