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