文章目录
前言
因为本人主要是学习后端Java的,前端呢只是了解一点点基础语法,所以本篇文章中可能会显得有一些不专业,所以呢,请大家多多包涵。
对于前后端交互的部分,我使用的最多的就是通过 Ajax 来像后端发送 HTTP 请求,但是呢,众所周知,Ajax 默认是不直接支持文件的下载的(即,它不能直接触发浏览器的下载管理器),,你通常需要将文件内容作为某种形式的数据(如Base64编码的字符串或Blob)返回,并在前端处理这些数据以触发下载或显示文件内容。
那么这篇文章,我将介绍如何对后端即将传输的文件做处理,以至于我们的前端能够得到这个文件。
如果文件可以通过URL访问
如果我们要上传的问价可以通过 URL 访问的话,那么我们就可以使用
UrlResource
来对文件进行处理:
importorg.springframework.core.io.Resource;importorg.springframework.core.io.UrlResource;importorg.springframework.http.HttpHeaders;importorg.springframework.http.MediaType;importorg.springframework.http.ResponseEntity;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importjava.net.MalformedURLException;importjava.nio.file.Paths;@RestControllerpublicclassFileDownloadController{@GetMapping("/download")publicResponseEntity<Resource>downloadFile(@RequestParamString fileName)throwsMalformedURLException{// 假设文件存储在服务器上的某个目录 String filePath ="/path/to/your/files/"+ fileName;Resource file =newUrlResource(filePath);if(file.exists()|| file.isReadable()){// 设置HTTP头以支持文件下载 HttpHeaders headers =newHttpHeaders();
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");returnResponseEntity.ok().headers(headers).contentType(MediaType.APPLICATION_OCTET_STREAM).body(file);}else{// 处理文件不存在或不可读的情况 returnResponseEntity.notFound().build();}}}
- 设置了Content-Disposition为attachment,这通常用于提示浏览器将响应作为文件下载。但是,如果你希望图片直接在浏览器中显示,可能不需要这个设置。
- CACHE_CONTROL 这个请求头就是缓存控制
- expires 过期时间
注意我们这个类的返回类型需是
ResponseEntity<>
,该类用于构建 HTTP 响应。响应中的 contentType 用来设置我们返回的 body 是什么类型,MediaType 类中有很多的静态类型:
publicclassMediaTypeextendsMimeTypeimplementsSerializable{privatestaticfinallong serialVersionUID =2069937152339670231L;publicstaticfinalMediaTypeALL=newMediaType("*","*");publicstaticfinalStringALL_VALUE="*/*";publicstaticfinalMediaTypeAPPLICATION_ATOM_XML=newMediaType("application","atom+xml");publicstaticfinalStringAPPLICATION_ATOM_XML_VALUE="application/atom+xml";publicstaticfinalMediaTypeAPPLICATION_CBOR=newMediaType("application","cbor");publicstaticfinalStringAPPLICATION_CBOR_VALUE="application/cbor";publicstaticfinalMediaTypeAPPLICATION_FORM_URLENCODED=newMediaType("application","x-www-form-urlencoded");publicstaticfinalStringAPPLICATION_FORM_URLENCODED_VALUE="application/x-www-form-urlencoded";publicstaticfinalMediaTypeAPPLICATION_GRAPHQL=newMediaType("application","graphql+json");publicstaticfinalStringAPPLICATION_GRAPHQL_VALUE="application/graphql+json";publicstaticfinalMediaTypeAPPLICATION_JSON=newMediaType("application","json");publicstaticfinalStringAPPLICATION_JSON_VALUE="application/json";/** @deprecated */@DeprecatedpublicstaticfinalMediaTypeAPPLICATION_JSON_UTF8;/** @deprecated */@DeprecatedpublicstaticfinalStringAPPLICATION_JSON_UTF8_VALUE="application/json;charset=UTF-8";publicstaticfinalMediaTypeAPPLICATION_OCTET_STREAM;publicstaticfinalStringAPPLICATION_OCTET_STREAM_VALUE="application/octet-stream";publicstaticfinalMediaTypeAPPLICATION_PDF;publicstaticfinalStringAPPLICATION_PDF_VALUE="application/pdf";publicstaticfinalMediaTypeAPPLICATION_PROBLEM_JSON;publicstaticfinalStringAPPLICATION_PROBLEM_JSON_VALUE="application/problem+json";/** @deprecated */@DeprecatedpublicstaticfinalMediaTypeAPPLICATION_PROBLEM_JSON_UTF8;/** @deprecated */@DeprecatedpublicstaticfinalStringAPPLICATION_PROBLEM_JSON_UTF8_VALUE="application/problem+json;charset=UTF-8";publicstaticfinalMediaTypeAPPLICATION_PROBLEM_XML;publicstaticfinalStringAPPLICATION_PROBLEM_XML_VALUE="application/problem+xml";publicstaticfinalMediaTypeAPPLICATION_RSS_XML;publicstaticfinalStringAPPLICATION_RSS_XML_VALUE="application/rss+xml";publicstaticfinalMediaTypeAPPLICATION_NDJSON;publicstaticfinalStringAPPLICATION_NDJSON_VALUE="application/x-ndjson";/** @deprecated */@DeprecatedpublicstaticfinalMediaTypeAPPLICATION_STREAM_JSON;/** @deprecated */@DeprecatedpublicstaticfinalStringAPPLICATION_STREAM_JSON_VALUE="application/stream+json";publicstaticfinalMediaTypeAPPLICATION_XHTML_XML;publicstaticfinalStringAPPLICATION_XHTML_XML_VALUE="application/xhtml+xml";publicstaticfinalMediaTypeAPPLICATION_XML;publicstaticfinalStringAPPLICATION_XML_VALUE="application/xml";publicstaticfinalMediaTypeIMAGE_GIF;publicstaticfinalStringIMAGE_GIF_VALUE="image/gif";publicstaticfinalMediaTypeIMAGE_JPEG;publicstaticfinalStringIMAGE_JPEG_VALUE="image/jpeg";publicstaticfinalMediaTypeIMAGE_PNG;publicstaticfinalStringIMAGE_PNG_VALUE="image/png";publicstaticfinalMediaTypeMULTIPART_FORM_DATA;publicstaticfinalStringMULTIPART_FORM_DATA_VALUE="multipart/form-data";publicstaticfinalMediaTypeMULTIPART_MIXED;publicstaticfinalStringMULTIPART_MIXED_VALUE="multipart/mixed";publicstaticfinalMediaTypeMULTIPART_RELATED;publicstaticfinalStringMULTIPART_RELATED_VALUE="multipart/related";publicstaticfinalMediaTypeTEXT_EVENT_STREAM;publicstaticfinalStringTEXT_EVENT_STREAM_VALUE="text/event-stream";publicstaticfinalMediaTypeTEXT_HTML;publicstaticfinalStringTEXT_HTML_VALUE="text/html";publicstaticfinalMediaTypeTEXT_MARKDOWN;publicstaticfinalStringTEXT_MARKDOWN_VALUE="text/markdown";publicstaticfinalMediaTypeTEXT_PLAIN;publicstaticfinalStringTEXT_PLAIN_VALUE="text/plain";publicstaticfinalMediaTypeTEXT_XML;publicstaticfinalStringTEXT_XML_VALUE="text/xml";privatestaticfinalStringPARAM_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。
functiondownloadFile(url, fileName){fetch(url,{method:'post',// 可以添加其他必要的请求头,如认证信息等 headers:{// 示例:'Authorization': 'Bearer your_token_here' },// 告诉浏览器我们期望的响应类型是一个Blob responseType:'blob'}).then(response=>{// 检查响应是否成功 if(!response.ok){thrownewError('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")publicResponseEntity<Resource>getUserPic(String fileName){//获取到存储在文件系统中的文件Resource resource =newFileSystemResource(Constant.path + fileName);returnResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).header(HttpHeaders.CONTENT_DISPOSITION,"attachment; filename="+ resource.getFilename()).body(resource);}
然后前端呢就还是通过 fetch 来为文件创建一个指向该文件的URL,就可以通过这个URL访问了。
如果使用了AOP对返回结果做了处理
如果我们的Spring使用了AOP来对返回结果进行了统一处理的话,对于返回的
ResponseEntity<>
我们还需要做出调整:
@ControllerAdvicepublicclassResponseAdviceimplementsResponseBodyAdvice{@AutowiredprivateObjectMapper objectMapper;@Overridepublicbooleansupports(MethodParameter returnType,Class converterType){returntrue;}@OverridepublicObjectbeforeBodyWrite(Object body,MethodParameter returnType,MediaType selectedContentType,Class selectedConverterType,ServerHttpRequest request,ServerHttpResponse response){//调整在这里,如果返回的类型是Resource的时候就直接返回if(body instanceofResource){return body;}if(body instanceofString){try{return objectMapper.writeValueAsString(body);}catch(JsonProcessingException e){thrownewRuntimeException(e);}}elseif(body instanceofResult){return body;}else{returnResult.success(body);}}}
版权归原作者 不能再留遗憾了 所有, 如有侵权,请联系我们删除。