下载/导出问题(统一返回):No converter for xxx with preset Content-Type ‘application/octet-stream;charset=UTF-8

1.序

下载接口正常返回下载数据,文件流关闭后响应返回success,控制台报错

全部内容:
大概意思是没有转换器。内含内含预设的内容类型“application/octet-stream;charset=UTF-8”

org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class com.cn.common.AjaxResult] with preset Content-Type 'application/octet-stream;charset=UTF-8'
	at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:319)
	at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:194)
	at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:78)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:135)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:973)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1010)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:902)

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

2.源码

@GetMapping("/d1")
    public AjaxResult<?> d1(HttpServletResponse httpServletResponse){
        DownloadHuTool.download2_2(httpServletResponse);
        return AjaxResult.success();
    }
/**
     * HuTool HttpRequest方式
     * 下载文件传给前端
     *
     * @param httpServletResponse httpServletResponse
     */
    public static void download2_2(HttpServletResponse httpServletResponse) {
        try {
            // 文件名
            String fileName = "AA.png";
            // 获取文件
            HttpResponse execute = HttpRequest.get("https://emojimix.app/emojimixfusion/1_1.png").execute();
            // 设置响应头信息,告诉前端浏览器下载文件
            httpServletResponse.setContentType("application/octet-stream;charset=UTF-8");
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
            // httpServletResponse.setHeader("Content-Length", String.valueOf(count)); 传出文件长度,这样前端可以实现下载进度条
            // 获取输出流进行写入数据
            OutputStream outputStream = httpServletResponse.getOutputStream();
            byte[] bytes = execute.bodyBytes();
            // 将字节复制到输出流
            outputStream.write(bytes);
            // 关闭流资源
            outputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
package com.cn.common;

/**
 * 统一返回类
 *
 * @author Yph
 */
public class AjaxResult<T> {

    /**
     * 状态码
     */
    private Integer code;

    /**
     * 返回内容
     */
    private String msg;

    /**
     * 数据对象
     */
    private T data;

    public AjaxResult(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public AjaxResult(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "AjaxResult{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }

    public static <T> AjaxResult<T> success() {
        return new AjaxResult<>(HttpStatus.SUCCESS, "操作成功", null);
    }

    public static <T> AjaxResult<T> success(T data) {
        return new AjaxResult<>(HttpStatus.SUCCESS, "操作成功", data);
    }

    public static <T> AjaxResult<T> success(String msg, T data) {
        return new AjaxResult<>(HttpStatus.SUCCESS, msg, data);
    }

    public static <T> AjaxResult<T> success(Integer code, String msg, T data) {
        return new AjaxResult<>(code, msg, data);
    }

    public static <T> AjaxResult<T> error(T data) {
        return new AjaxResult<>(HttpStatus.ERROR, "操作失败", data);
    }

    public static <T> AjaxResult<T> error(String msg, T data) {
        return new AjaxResult<>(HttpStatus.ERROR, msg, data);
    }

    public static <T> AjaxResult<T> error(String msg) {
        return new AjaxResult<>(HttpStatus.ERROR, msg, null);
    }

    public static <T> AjaxResult<T> error(T data, Integer code, String msg) {
        return new AjaxResult<>(code, msg, data);
    }
}

3.解决问题

问题原因:由于下载控制器调用的下载实现类,控制器使用了统一返回类AjaxResult对象,但同时设置了Content-Type为application/octet-stream;charset=UTF-8,这表示你试图返回一个二进制文件流。在Spring框架中,当返回的对象不是ResponseEntity或HttpEntity时,Spring会试图找到一个合适的HttpMessageConverter将返回的对象转换为响应体。

在你的情况下,Spring试图将AjaxResult对象转换为application/octet-stream;charset=UTF-8格式的响应体,但是没有找到合适的转换器,因此抛出了HttpMessageNotWritableException异常。

3.1.方式一(推荐)

此方式照样返回状态信息,非常推荐
其实前端响应JSON信息和数据流只能二选一,但是有异常直接就响应了非常好。

	/**
     * 解决方式一(推荐使用这种)
     * 手动返回状态消息
     */
    @GetMapping("/d1_1")
    public void d1_1(HttpServletResponse httpServletResponse) {
        try {
            DownloadHuTool.download2_2(httpServletResponse);
        } catch (Exception e) {
            e.printStackTrace();
            httpServletResponse.reset();
            AjaxResult<T> result = new AjaxResult<>(HttpStatus.ERROR, e.getMessage());
            String json = JSON.toJSONString(result);
            ServletUtils.renderString(httpServletResponse, json);
        }
    }
/**
     * 将字符串渲染到客户端
     *
     * @param response 渲染对象
     * @param string   待渲染的字符串
     */
    public static void renderString(HttpServletResponse response, String string) {
        try {
            response.setStatus(200);
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(string);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3.2.方式二(一般)

不要返回信息,直接返回null,不好在于没法返回状态信息

	/**
     * 解决方式二
     * 不返回null会报异常
     */
    @GetMapping("/d1_1")
    public AjaxResult<?> d1_2(HttpServletResponse httpServletResponse) {
        DownloadHuTool.download2_2(httpServletResponse);
        return null;
    }

3.3.方式三(NO)

将下载文件的逻辑放入一个单独的方法中,并且不要返回任何值。然后,在控制器方法中调用这个方法,并返回一个合适的响应,控制器方法会返回一个ResponseEntity对象,该对象包含了文件内容以及适当的响应头。注意,这个方法不会返回AjaxResult对象,因此不会引发上述异常。

改造原先的代码:

@GetMapping("/d1_2")
    public ResponseEntity<ByteArrayResource> d1_2() {
        // 文件名
        String fileName = "AA.png";
        // 获取文件
        HttpResponse execute = HttpRequest.get("https://emojimix.app/emojimixfusion/1_1.png").execute();
        // 设置响应头信息,告诉前端浏览器下载文件
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.set("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
        // 获取输出流进行写入数据
        byte[] bytes = execute.bodyBytes();
        ByteArrayResource resource = new ByteArrayResource(bytes);
        return ResponseEntity.ok().headers(headers).contentLength(bytes.length).body(resource);
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值