对于前端发送的请求,Spring如何返回一个文件

不能再留遗憾了 2024-09-02 16:33:01 阅读 85

文章目录

前言如果文件可以通过URL访问什么是fetch

如果文件无法通过URL访问到如果使用了AOP对返回结果做了处理

前言

因为本人主要是学习后端Java的,前端呢只是了解一点点基础语法,所以本篇文章中可能会显得有一些不专业,所以呢,请大家多多包涵。

对于前后端交互的部分,我使用的最多的就是通过 Ajax 来像后端发送 HTTP 请求,但是呢,众所周知,Ajax 默认是不直接支持文件的下载的(即,它不能直接触发浏览器的下载管理器),,你通常需要将文件内容作为某种形式的数据(如Base64编码的字符串或Blob)返回,并在前端处理这些数据以触发下载或显示文件内容。

那么这篇文章,我将介绍如何对后端即将传输的文件做处理,以至于我们的前端能够得到这个文件。

如果文件可以通过URL访问

如果我们要上传的问价可以通过 URL 访问的话,那么我们就可以使用 <code>UrlResource 来对文件进行处理:

import org.springframework.core.io.Resource;

import org.springframework.core.io.UrlResource;

import org.springframework.http.HttpHeaders;

import org.springframework.http.MediaType;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

import java.net.MalformedURLException;

import java.nio.file.Paths;

@RestController

public class FileDownloadController {

@GetMapping("/download")

public ResponseEntity<Resource> downloadFile(@RequestParam String fileName) throws MalformedURLException {

// 假设文件存储在服务器上的某个目录

String filePath = "/path/to/your/files/" + fileName;

Resource file = new UrlResource(filePath);

if (file.exists() || file.isReadable()) {

// 设置HTTP头以支持文件下载

HttpHeaders headers = new HttpHeaders();

headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"");

headers.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate");

headers.add(HttpHeaders.PRAGMA, "no-cache");

headers.add(HttpHeaders.EXPIRES, "0");

return ResponseEntity.ok()

.headers(headers)

.contentType(MediaType.APPLICATION_OCTET_STREAM)

.body(file);

} else {

// 处理文件不存在或不可读的情况

return ResponseEntity.notFound().build();

}

}

}

设置了Content-Disposition为attachment,这通常用于提示浏览器将响应作为文件下载。但是,如果你希望图片直接在浏览器中显示,可能不需要这个设置。CACHE_CONTROL 这个请求头就是缓存控制expires 过期时间

注意我们这个类的返回类型需是 ResponseEntity<>,该类用于构建 HTTP 响应。响应中的 contentType 用来设置我们返回的 body 是什么类型,MediaType 类中有很多的静态类型:

public class MediaType extends MimeType implements Serializable {

private static final long serialVersionUID = 2069937152339670231L;

public static final MediaType ALL = new MediaType("*", "*");

public static final String ALL_VALUE = "*/*";

public static final MediaType APPLICATION_ATOM_XML = new MediaType("application", "atom+xml");

public static final String APPLICATION_ATOM_XML_VALUE = "application/atom+xml";

public static final MediaType APPLICATION_CBOR = new MediaType("application", "cbor");

public static final String APPLICATION_CBOR_VALUE = "application/cbor";

public static final MediaType APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded");

public static final String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded";

public static final MediaType APPLICATION_GRAPHQL = new MediaType("application", "graphql+json");

public static final String APPLICATION_GRAPHQL_VALUE = "application/graphql+json";

public static final MediaType APPLICATION_JSON = new MediaType("application", "json");

public static final String APPLICATION_JSON_VALUE = "application/json";

/** @deprecated */

@Deprecated

public static final MediaType APPLICATION_JSON_UTF8;

/** @deprecated */

@Deprecated

public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8";

public static final MediaType APPLICATION_OCTET_STREAM;

public static final String APPLICATION_OCTET_STREAM_VALUE = "application/octet-stream";

public static final MediaType APPLICATION_PDF;

public static final String APPLICATION_PDF_VALUE = "application/pdf";

public static final MediaType APPLICATION_PROBLEM_JSON;

public static final String APPLICATION_PROBLEM_JSON_VALUE = "application/problem+json";

/** @deprecated */

@Deprecated

public static final MediaType APPLICATION_PROBLEM_JSON_UTF8;

/** @deprecated */

@Deprecated

public static final String APPLICATION_PROBLEM_JSON_UTF8_VALUE = "application/problem+json;charset=UTF-8";

public static final MediaType APPLICATION_PROBLEM_XML;

public static final String APPLICATION_PROBLEM_XML_VALUE = "application/problem+xml";

public static final MediaType APPLICATION_RSS_XML;

public static final String APPLICATION_RSS_XML_VALUE = "application/rss+xml";

public static final MediaType APPLICATION_NDJSON;

public static final String APPLICATION_NDJSON_VALUE = "application/x-ndjson";

/** @deprecated */

@Deprecated

public static final MediaType APPLICATION_STREAM_JSON;

/** @deprecated */

@Deprecated

public static final String APPLICATION_STREAM_JSON_VALUE = "application/stream+json";

public static final MediaType APPLICATION_XHTML_XML;

public static final String APPLICATION_XHTML_XML_VALUE = "application/xhtml+xml";

public static final MediaType APPLICATION_XML;

public static final String APPLICATION_XML_VALUE = "application/xml";

public static final MediaType IMAGE_GIF;

public static final String IMAGE_GIF_VALUE = "image/gif";

public static final MediaType IMAGE_JPEG;

public static final String IMAGE_JPEG_VALUE = "image/jpeg";

public static final MediaType IMAGE_PNG;

public static final String IMAGE_PNG_VALUE = "image/png";

public static final MediaType MULTIPART_FORM_DATA;

public static final String MULTIPART_FORM_DATA_VALUE = "multipart/form-data";

public static final MediaType MULTIPART_MIXED;

public static final String MULTIPART_MIXED_VALUE = "multipart/mixed";

public static final MediaType MULTIPART_RELATED;

public static final String MULTIPART_RELATED_VALUE = "multipart/related";

public static final MediaType TEXT_EVENT_STREAM;

public static final String TEXT_EVENT_STREAM_VALUE = "text/event-stream";

public static final MediaType TEXT_HTML;

public static final String TEXT_HTML_VALUE = "text/html";

public static final MediaType TEXT_MARKDOWN;

public static final String TEXT_MARKDOWN_VALUE = "text/markdown";

public static final MediaType TEXT_PLAIN;

public static final String TEXT_PLAIN_VALUE = "text/plain";

public static final MediaType TEXT_XML;

public static final String TEXT_XML_VALUE = "text/xml";

private static final String PARAM_QUALITY_FACTOR = "q";

}

大家可以根据自己要返回的文件的具体类型来选择。

后端对文件进行处理了之后,前端也是需要做出调整的,由于 Ajax 默认不支持文件的下载,所以我们选择使用 fetch 来作为 web api。

什么是fetch

这里的解释来自于百度:

fetch 是 Web API 的一部分,它提供了一种简单、逻辑清晰的方式来跨网络异步获取资源(包括文件、网络请求等)。fetch API 返回一个 Promise,这个 Promise 解析为一个 Response 对象,该对象包含来自服务器的各种响应信息,比如响应头、状态码等,并且允许你访问响应体(response body)的内容。

与 XMLHttpRequest 相比,fetch 提供了一个更现代、更简洁的API来访问和操作网络请求和响应。fetch 支持 Promise API,这使得异步逻辑更加容易编写和理解。

fetch 处理文件更加的方便,所以这里我们选择使用 fetch。

function downloadFile(url, fileName) {

fetch(url, {

method: 'post',

// 可以添加其他必要的请求头,如认证信息等

headers: {

// 示例:'Authorization': 'Bearer your_token_here'

},

// 告诉浏览器我们期望的响应类型是一个Blob

responseType: 'blob'

})

.then(response => {

// 检查响应是否成功

if (!response.ok) {

throw new Error('Network response was not ok');

}

// 返回Blob对象

return response.blob();

})

.then(blob => {

// 创建一个指向该Blob的URL

// 我们可以通过这个生成的URL访问到这个文件

const url = window.URL.createObjectURL(blob);

// 我这里就直接将图片的URL给用了

let pic = document.querySelector('.main .left .user .picture')

pic.style.backgroundImage = 'url(' + url + ')'

// 这个用于将生成的URL给清除掉,我们这里可以先不清,

//如果清除了的话,前端可能无法通过这个URL获取到这个文件,

//我们可以在关闭页面的时候调用这个方法

// 清理工作

// window.URL.revokeObjectURL(url);

})

.catch(error => {

console.error('There has been a problem with your fetch operation:', error);

});

}

如果文件无法通过URL访问到

如果文件不是通过URL访问到的,例如它们存储在文件系统中的话,就需要依靠 FileSystemResource 等其他资源类。

@RequestMapping("/getUserPic")

public ResponseEntity<Resource> getUserPic(String fileName) {

//获取到存储在文件系统中的文件

Resource resource = new FileSystemResource(Constant.path + fileName);

return ResponseEntity.ok()

.contentType(MediaType.IMAGE_JPEG)

.header(HttpHeaders.CONTENT_DISPOSITION,"attachment; filename=" + resource.getFilename())

.body(resource);

}

然后前端呢就还是通过 fetch 来为文件创建一个指向该文件的URL,就可以通过这个URL访问了。

如果使用了AOP对返回结果做了处理

如果我们的Spring使用了AOP来对返回结果进行了统一处理的话,对于返回的 ResponseEntity<> 我们还需要做出调整:

@ControllerAdvice

public class ResponseAdvice implements ResponseBodyAdvice {

@Autowired

private ObjectMapper objectMapper;

@Override

public boolean supports(MethodParameter returnType, Class converterType) {

return true;

}

@Override

public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

//调整在这里,如果返回的类型是Resource的时候就直接返回

if (body instanceof Resource) {

return body;

}

if (body instanceof String) {

try {

return objectMapper.writeValueAsString(body);

} catch (JsonProcessingException e) {

throw new RuntimeException(e);

}

}else if (body instanceof Result) {

return body;

}else {

return Result.success(body);

}

}

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。