文章目录
前言如果文件可以通过URL访问什么是fetch 如果文件无法通过URL访问到如果使用了AOP对返回结果做了处理
前言
因为本人主要是学习后端Java的,前端呢只是了解一点点基础语法,所以本篇文章中可能会显得有一些不专业,所以呢,请大家多多包涵。
对于前后端交互的部分,我使用的最多的就是通过 Ajax 来像后端发送 HTTP 请求,但是呢,众所周知,Ajax 默认是不直接支持文件的下载的(即,它不能直接触发浏览器的下载管理器),,你通常需要将文件内容作为某种形式的数据(如Base64编码的字符串或Blob)返回,并在前端处理这些数据以触发下载或显示文件内容。
那么这篇文章,我将介绍如何对后端即将传输的文件做处理,以至于我们的前端能够得到这个文件。
如果文件可以通过URL访问
如果我们要上传的问价可以通过 URL 访问的话,那么我们就可以使用 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<>
我们还需要做出调整:
@ControllerAdvicepublic 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); } }}