开发者

前端发送的请求Spring如何返回一个文件详解

目录
  • 前言
  • 如果文件可以通过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_PRjavascriptOBLEM_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"javascript;
            public static fina编程客栈l 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 等其他资源类。

        @Request编程客栈Mapping("/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对返回结果做了处理

        如果我们的Springandroid使用了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);
                }
            }
        }

        总结 

        到此这篇关于前端发送的请求Spring如何返回一个文件的文章就介绍到这了,更多相关前端发送请求Spring返回文件内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

        0

        上一篇:

        下一篇:

        精彩评论

        暂无评论...
        验证码 换一张
        取 消

        最新开发

        开发排行榜