跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Java大前端java

Vue3 + Spring Boot 文件下载异常错误友好提示处理

解决 Vue3 前端配合 Spring Boot 后端进行文件下载时,业务异常(如模板不存在)无法友好提示的问题。原因为后端返回资源流类型导致异常 JSON 被当作 Blob 处理,且未统一错误响应格式。通过全局异常处理器区分请求类型返回 JSON 或错误文件,并优化前端 Axios 拦截器解析 Blob 内容,实现了准确的错误信息展示及 UTF-8 编码支持。

落日余晖发布于 2026/4/6更新于 2026/10/1999 浏览

Vue3 + Spring Boot 文件下载异常错误友好提示处理

问题描述

在进行文件下载功能开发时,当前端请求后端接口返回业务异常(如模板文件不存在)时,前端仅显示通用的'文件下载失败'提示,无法展示具体的业务错误信息(如'模板文件不存在')。

原因分析

  1. 前端响应类型:前端 Axios 请求设置了 responseType: 'blob',导致后端返回的 JSON 格式错误信息被当作二进制流处理。
  2. 后端返回类型冲突:Controller 方法返回类型为 ResponseEntity<Resource>,成功时返回文件流,但异常时若直接返回 JSON 对象会导致类型不匹配或前端无法正确解析。
  3. 编码问题:错误文件响应未明确指定 UTF-8 字符集,导致中文乱码。

解决方案

1. 后端优化:全局异常处理器

使用 @RestControllerAdvice 统一处理异常。针对文件下载请求和普通请求返回不同格式的响应,确保错误信息可被前端识别。

GlobalExceptionHandler.java
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    
    @ExceptionHandler(TemplateNotFoundException.class)
    public Object handleTemplateNotFoundException(TemplateNotFoundException e, HttpServletRequest request) {
        log.error("模板文件不存在:{}", e.getMessage(), e);
        if (ErrorFileResponseUtils.isFileDownloadRequest(request)) {
            return ErrorFileResponseUtils.createJsonErrorResponse("模板文件不存在", HttpStatus.NOT_FOUND);
        }
        return Result.error("模板文件不存在");
    }

    @ExceptionHandler(ResourceNotFoundException.class)
    public Object handleResourceNotFoundException(ResourceNotFoundException e, HttpServletRequest request) {
        log.error("资源文件不存在:{}", e.getMessage(), e);
        if (ErrorFileResponseUtils.isFileDownloadRequest(request)) {
            return ErrorFileResponseUtils.createJsonErrorResponse("文件不存在", HttpStatus.NOT_FOUND);
        }
        return Result.error("文件不存在");
    }

    @ExceptionHandler(Exception.class)
    public Object handleException(Exception e, HttpServletRequest request) {
        log.error("系统异常:{}", e.getMessage(), e);
        if (ErrorFileResponseUtils.isFileDownloadRequest(request)) {
            return ErrorFileResponseUtils.createJsonErrorResponse("操作失败", HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return Result.error("操作失败");
    }
}
ErrorFileResponseUtils.java

确保错误响应包含正确的 Content-Type 和字符集。

public static ResponseEntity<Result<Object>> createJsonErrorResponse(String message, HttpStatus status) {
    Result<Object> result = Result.error(message);
    return ResponseEntity
            .status(status)
            .contentType(MediaType.APPLICATION_JSON)
            .body(result);
}

public static boolean isFileDownloadRequest(HttpServletRequest request) {
    String path = request.getRequestURI();
    if (path.contains("/export") || path.contains("/download")) {
        return true;
    }
    String acceptHeader = request.getHeader(HttpHeaders.ACCEPT);
    return acceptHeader != null && acceptHeader.contains(MediaType.APPLICATION_OCTET_STREAM_VALUE);
}

2. 前端优化:Axios 拦截器

修改 Axios 响应拦截器,在捕获 blob 错误时尝试解析内容。优先解析 JSON,若失败则提取文本内容。

src/utils/request.ts
import axios from "axios";
import { ElMessage } from "element-plus";

const instance = axios.create({ baseURL: "/api" });

instance.interceptors.response.use(
    async (response) => {
        // 正常逻辑...
        return response.data;
    },
    async (error) => {
        // 401 未授权
        if (error.response?.status === 401) {
            router.push("/login");
            return Promise.reject(error);
        }

        // 处理文件下载的错误响应
        if (
            error.config?.responseType === "blob" &&
            error.response?.data instanceof Blob &&
            error.response?.data.size < 1024
        ) {
            try {
                const errorText = await error.response.data.text();
                try {
                    const errorJson = JSON.parse(errorText);
                    const message = errorJson.message || errorJson.msg || "文件下载失败";
                    ElMessage.error(message);
                } catch {
                    // 非 JSON 格式,尝试提取核心错误信息
                    let message = errorText;
                    const match = errorText.match(/^\[.*?\]\s*(.*)/);
                    if (match && match[1]) {
                        message = match[1];
                    }
                    ElMessage.error(message || "文件下载失败");
                }
            } catch {
                ElMessage.error("文件下载失败");
            }
            return Promise.reject(error);
        }

        // 普通错误响应
        const errorData = error.response?.data || {};
        ElMessage.error(errorData.msg || errorData.message || "服务异常!");
        return Promise.reject(error);
    }
);

export default instance;

3. 控制器代码调整

移除 Controller 中的 try-catch,直接抛出异常由全局处理器处理。

CapitalInfoController.java
@PostMapping("/export-data-by-template")
public ResponseEntity<Resource> exportDataByTemplate(@RequestBody @Valid CapitalInfoQueryDTO queryDTO) throws IOException {
    String filePath = capitalInfoService.exportFileByTemplate(queryDTO);
    Path path = Paths.get(filePath);
    Resource resource = new UrlResource(path.toUri());

    if (!resource.exists()) {
        throw new ResourceNotFoundException("资源文件不存在");
    }

    return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .header("Content-Disposition", "attachment;filename=" + URLEncoder.encode(FileUtils.getFileName(filePath), StandardCharsets.UTF_8))
            .body(resource);
}

总结

通过统一后端异常响应格式并增强前端对 Blob 错误内容的解析能力,实现了文件下载场景下业务异常的精准提示,同时解决了中文乱码问题。

目录

  1. Vue3 + Spring Boot 文件下载异常错误友好提示处理
  2. 问题描述
  3. 原因分析
  4. 解决方案
  5. 1. 后端优化:全局异常处理器
  6. GlobalExceptionHandler.java
  7. ErrorFileResponseUtils.java
  8. 2. 前端优化:Axios 拦截器
  9. src/utils/request.ts
  10. 3. 控制器代码调整
  11. CapitalInfoController.java
  12. 总结

更多推荐文章

查看全部
  • JDK 21 时代 Java 代码安全:从命名混淆到深度防护
  • VS Code 远程连接服务器后 GitHub Copilot 无法使用的排查与修复
  • React 基础:JSX 语法细节与表单组件实战
  • 二分算法实战:A-B 数对与高考志愿录取匹配
  • 用 Python 构建跨平台 GUI 与 Web 应用:Flet 框架实战
  • C++ 实现红黑树:深入 STL map 底层原理
  • Python 性能分析实战:从 cProfile 到火焰图,精准定位性能瓶颈
  • Kubernetes 边缘 AI 部署实战与最佳实践
  • 计算机视觉基础、模型架构与实战开发指南
  • 跨平台文件传输:WebDAV + Rclone
  • AI 辅助 9·1 免费版安装:智能检测与修复方案
  • Python 构建跨平台前端界面:Flet 库详解
  • Pi Coding Agent 编码工具架构模式解析
  • 网络安全学习计划与就业方向指南
  • Vue2 纯前端对接海康威视摄像头实时视频预览
  • 机器人动力学:牛顿欧拉法推导与详解
  • 代码片段智能推荐的核心算法模型解析
  • TapNow 影视级 AI 视频平台实测:导演级精准控制与物理一致性
  • 开源 AI 编程工具对比:Superpowers 技能库与 OpenSpec 规范驱动
  • Whisper Large v3 多语言语音识别 Web 服务部署实战

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online