使用Spring进行文件的上传和下载

概览

  • 使用Spring进行文件的上传和下载
    • Spring上传文件接口设计
    • dubbo接口设计
      • 上传文件流的RPC的接口设计
    • Spring文件下载接口设计
    • dubbo接口设计
      • 下载文件流的RPC的接口设计
    • spring上传文件大小控制

使用Spring进行文件的上传和下载

本文主要介绍在Spring框架下面调用微服务的dubbo rpc接口进行文件的上传和下载,以及记录在实现过程中遇到的一些容易出错的地方。

Spring上传文件接口设计

contoller层的代码实现如下所示:

    @PostMapping("/submitEvidence")public BaseResponse<?> submitEvidence(@RequestParam("id") Long id, @RequestParam("label") String label,@RequestParam(value = "file") MultipartFile file) {uploadEvidence(id, label, file);return BaseResponse.success().errorMsg("操作成功").build();}}

使用postman请求上传文件接口,具体参数如下图所示:postman请求截图
Service层代码实现如下所示:

public void uploadEvidence(Long takeDownId, String label, MultipartFile multipartFile) {if(Objects.isNull(multipartFile)) {throw new RunTimeException("上传的文件不能为空");}String fileName = multipartFile.getOriginalFilename();InputStream file = null;try {file = multipartFile.getInputStream();} catch (IOException e) {}byte[] fileBytes = new byte[20 * 1024 * 1024];InputStream inputStream = null;ByteArrayOutputStream outputStream = null;try {outputStream =  new ByteArrayOutputStream();inputStream = multipartFile.getInputStream();try {byte[] buffer = new byte[1024];int read = inputStream.read(buffer);while (read != -1) {outputStream.write(buffer, 0, read);read = inputStream.read(buffer);}} catch (Exception e) {log.error("处理返回值失败" + e.getMessage());throw new RunTimeException("上传文件失败");} finally {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}outputStream.flush();fileBytes = outputStream.toByteArray();} catch (IOException e) {}finally {if(outputStream != null) {try {outputStream.close();} catch (IOException e1) {e1.printStackTrace();throw new RuntimeException("上传文件失败");}}}UploadEvidenceNetCraftReq req = UploadEvidenceNetCraftReq.builder().takeDownId(takeDownId.intValue()).label(label).fileName(fileName).fileBytes(fileBytes).build();// outVendor是一个dubbo框架下的rpc服务,用于上传文件BaseResponse baseResponse = outerVendorsService.uploadEvidence(req);...}

dubbo接口设计

上传文件流的RPC的接口设计

我们构建的RPC接口采用的是dubbo框架,最初始的接口设计,将HttpServletResponse作为接口的参数类型传参,结果报错
io.netty.handler.codec.EncoderException: java.lang.IllegalStateException: Serialized class

Dubbo报错:io.netty.handler.codec.EncoderException: java.lang.IllegalStateException: Serialized class

HttpServletResponse不能被dubbo作为接口参数序列化,于是转而求其次,将可序列化的类型byte[]作为传输流的参数
outerVendorsService服务提供的上传文件的rpc接口:uploadEvidence接口,具体代码设计如下所示:

BaseResponse<UploadEvidenceRsp> uploadEvidence(UploadEvidenceReq req);
@Data
@Builder
@Jacksonized
public class UploadEvidenceReq implements Serializable {private Integer takeDownId;//使用可序列化的byte数组作为入参private byte[] fileBytes;private String fileName;private String label;
}@Data
@Setter
@Getter
public class UploadEvidenceRsp implements Serializable {private Integer file_id;@JsonProperty("error_code")private String errorCode;@JsonProperty("error_message")private String errorMessage;
}

Spring文件下载接口设计

文件下载的相关接口有两种实现:一种是将HttpServletResponse作为controller层的传参引入,将流写入到HttpServletResponse中,然后返回前端,但是在使用过程中,直接在HttpServletResponse的示例中setHeader失败,于是转而选择构ResponseEntity的方式来进行http返回值的构造,具体实现如下所示:

@GetMapping("/searchForEvidence")
public ResponseEntity searchForEvidence(@RequestParam String id) {return searchForEvidence(id);
}

使用postman请求下载文件接口,具体参数如下图所示:
下载文件接口
Service层代码实现如下所示:

public ResponseEntity fetchEvidence(Long takeDownId){FetchEvidenceNetCraftReq req = FetchEvidenceNetCraftReq.builder().takeDownId(takeDownId.intValue()).build();BaseResponse<QueryEvidenceRsp> rsp = outerVendorsService.fetchEvidence(req);if(Objects.isNull(rsp)) {throw new RunTimeException("拉取文件失败");}byte[] outPutBytes = null;if(Objects.nonNull(rsp) && rsp.isSuccess() == true) {QueryEvidenceRsp evidenceRsp = rsp.getResult();if(Objects.nonNull(evidenceRsp.getErrorCode())){String message = "errorCode:" + evidenceRsp.getErrorCode() + ",errorMessage:" + evidenceRsp.getErrorMessage();throw new RunTimeException(message);}outPutBytes = evidenceRsp.getFileBytes();String fileName = evidenceRsp.getFileName();HttpHeaders responseHeaders = new HttpHeaders();responseHeaders.setContentType(MediaType.valueOf(MediaType.APPLICATION_OCTET_STREAM_VALUE));// 设置文件格式responseHeaders.setContentLength(outPutBytes.length);responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName);// 设置文件名return new ResponseEntity<>(outPutBytes, responseHeaders, HttpStatus.OK);}throw new RunTimeException("拉取文件失败");
}public class QueryEvidenceRsp implements Serializable {byte[] fileBytes;String fileName;String errorCode;String errorMessage;
}

dubbo接口设计

下载文件流的RPC的接口设计

参照之前的上传文件的设计,服务提供的下载文件的rpc接口设计:

@Data
@Setter
@Getter
public class QueryEvidenceRsp implements Serializable {byte[] fileBytes;String fileName;String errorCode;String errorMessage;
}@Data
@Builder
@Jacksonized
public class FetchEvidenceNetCraftReq implements Serializable {private Integer takeDownId;
}@Overridepublic BaseResponse fetchEvidence(FetchEvidenceNetCraftReq req) {if(Objects.isNull(netCraftConfig) || Objects.isNull(netCraftConfig.getAccessNetCraftDomain())) {log.error("netCraft config is not set");return BaseResponse.fail().errorCode(ErrorCode.BUSINESS_EXCEPTION.getCode()).errorMsg("netCraft config is not set").build();}String fetch_evidence = "https://" + netCraftConfig.getAccessNetCraftDomain()+ netCraftConfig.getFETCH_EVIDENCE();Map<String, String> headers = new HashMap<>();headers.put("content-type", "application/json");headers.put("Authorization", netCraftConfig.getAccessNetCraftAuthToken());ByteArrayOutputStream  outputStream = new ByteArrayOutputStream();byte[] fileBytes;String fileName;try {outputStream =  new ByteArrayOutputStream();fileName = getFromOctetStream(fetch_evidence + "?takedown_id="+ req.getTakeDownId(), headers, outputStream);if(Objects.isNull(fileName)) {return BaseResponse.fail().errorCode(ErrorCode.BUSINESS_EXCEPTION.getCode()).errorMsg("获取netcraft证据文件失败").build();}outputStream.flush();fileBytes = outputStream.toByteArray();} catch (Exception e) {log.error("获取证据文件失败:" + e.getMessage());return BaseResponse.fail().errorCode(ErrorCode.BUSINESS_EXCEPTION.getCode()).errorMsg("获取netcraft证据文件失败").build();} finally {if(outputStream != null) {try {outputStream.close();} catch (IOException e1) {e1.printStackTrace();}}}NetCraftQueryEvidenceRsp rsp = new NetCraftQueryEvidenceRsp();if(Objects.nonNull(fileName)) {NetCraftErrorMessageRsp errorMessageRsp = JsonUtils.fromCamelJson(fileName, NetCraftErrorMessageRsp.class);if(Objects.nonNull(errorMessageRsp)&& Objects.nonNull(errorMessageRsp.getErrorCode())&& Objects.nonNull(errorMessageRsp.getErrorMessage())) {rsp.setErrorCode(errorMessageRsp.getErrorCode());rsp.setErrorMessage(errorMessageRsp.getErrorMessage());return BaseResponse.success(rsp).build();}rsp.setFileBytes(fileBytes);rsp.setFileName(fileName);return BaseResponse.success(rsp).build();}return BaseResponse.fail().errorCode(ErrorCode.BUSINESS_EXCEPTION.getCode()).errorMsg("获取netcraft证据文件失败").build();}public static String getFromOctetStream(String url, Map<String, String> headers, OutputStream outputStream) {return getFromOctetStream(url, headers, OK_HTTP_CLIENT_30s, outputStream);}public static String getFromOctetStream(String url, Map<String, String> headers, String client, OutputStream outputStream) {Request.Builder requestBuilder = new Request.Builder();requestBuilder.url(url);if (headers != null && headers.size() > 0) {for (String s : headers.keySet()) {requestBuilder.addHeader(s, headers.get(s));}}requestBuilder.get();Request req = requestBuilder.build();try (Response response = okHttpClientMap.get(client).newCall(req).execute()) {log.info("okhttp send get,resp:{}", JsonUtils.toJson(response));if (null != response.body()) {InputStream inputStream = response.body().byteStream();try {byte[] buffer = new byte[1024];int read = inputStream.read(buffer);while (read != -1) {outputStream.write(buffer, 0, read);read = inputStream.read(buffer);}} catch (Exception e) {log.error("处理返回值失败" + e.getMessage());return null;} finally {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}String contentDisposition = response.header("Content-Disposition");if (Objects.isNull(contentDisposition)) {log.error("调用netcraft获取证据接口, 获取文件名失败");return outputStream.toString();}// 解析文件名return contentDisposition.substring(contentDisposition.indexOf("filename=") + 9);}

spring上传文件大小控制

在spring配置文件application.properties中,通过配置下面两个参数的值来限制文件的大小

spring.servlet.multipart.max-file-size=-1
spring.servlet.multipart.max-request-size=-1

spring.servlet.multipart.max-file-size配置限制上传单个文件的大小,为-1代表不限制
spring.servlet.multipart.max-request-size配置限制http中上传总文件的大小,为-1代表不限制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/1149.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode36: 有效的数独(Java)

题目&#xff1a; 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例…

vue 性能优化

data 层级不要太深 data 层级太深会增加响应式监听的计算&#xff0c;导致页面初次渲染时卡顿。 合理使用 v-show 和 v-if 频繁切换时&#xff0c;使用 v-show无需频繁切换时&#xff0c;使用 v-if 合理使用 computed computed 有缓存&#xff0c;data 不变时不会重新计算&…

一次Ambari安装记录

引言 Ambari是一个开源的Apache项目,它提供了一个直观易用的Web界面,用于管理、监控和配置Apache Hadoop集群。它是一个集群管理工具,可以帮助管理员轻松地部署、管理和监控Hadoop集群的各种组件,如HDFS、YARN、MapReduce、Hive、HBase等。通过Ambari,用户可以在集群中添…

pwn程序已经运行了,payload字节数组包含不可见字符,无法进行utf8编码,这个时候怎么输入?

假设./your_elf_program已经运行了,payload字节数组包含不可见字符,无法进行utf8编码,这个时候怎么输入? 如果 ./your_elf_program 已经在运行,并且你需要发送包含不可见字符的 payload 字节数组,你不能直接通过常规的命令行输入,因为终端不支持非 UTF-8 编码的数据输入…

OerOerlikonTCO1200欧瑞康LPCVD system操作使用说明

OerOerlikonTCO1200欧瑞康LPCVD system操作使用说明

DNS的背景工作原理和作用

1.背景: DNS的背景起源于20世纪60年代末的美国国防部高级研究计划局&#xff08;ARPA&#xff09;建立的试验性计算机网络ARPAnet。DNS&#xff0c;全称域名系统&#xff08;Domain Name System&#xff09;&#xff0c;是为了解决互联网上主机名与IP地址对应关系而发展起来的…

java中类A的所有实例方法都可以在A的子类中进行覆盖(Override)吗

在Java中&#xff0c;类A的所有非静态的实例方法&#xff08;也被称为实例成员方法&#xff09;理论上都可以在A的子类中进行覆盖&#xff08;Override&#xff09;&#xff0c;但这有几个重要的前提和注意事项&#xff1a; 访问权限&#xff1a;被覆盖的方法在父类中的访问权…

算法训练营day18

文章目录 一、找树左下角的值二、路径总和1 & 2从路径总和1 & 2体会什么情况下递归需要返回值&#xff0c;什么时候不需要返回值路径总和1初始递归DFSBFS路径总和2 三、从中序与后序遍历序列构造二叉树四、从前序和中序遍历序列构造二叉树 一、找树左下角的值 参考链接…

运行python脚本下载官网安装包进行安装

背景介绍&#xff1a;1.由于公司业务人员window系统没有管理员用户权限&#xff0c;使用的是普通用户权限登陆的&#xff0c;因此不能自己安装软件。但是有时候涉及到软件的大批量更新&#xff0c;人工一个一个的去安装&#xff0c;效率太低&#xff0c;人工成本太高&#xff0…

DQ-DETR: DETR WITH DYNAMIC QUERY FOR TINY OBJECTDETECTION 学习笔记

论文地址&#xff1a;https://arxiv.org/pdf/2404.03507.pdf 此DQ-DETR与IDEA提出的同名&#xff0c;该文主要集中于小目标的检测 尽管之前的类似DETR的方法在通用目标检测中取得了成功&#xff0c;但在小目标检测方面仍然具有挑战性&#xff0c;因为目标 Query 的位置信息并未…

LWIP开启ARP之后进入硬件错误中断

遇到个很奇怪的问题&#xff0c;如下图只要开启ARP之后&#xff0c;就会进入硬件错误中断&#xff0c;关掉就好了。 而无法开启ARP&#xff0c;就不能ping 通&#xff0c;所以必须要解决这个问题。 最终debug发现死在memcpy函数位置 这样原因就很好分析了&#xff0c; 共4个拷…

通过linux工具iftop命令查看视频监控平台是否收到监控摄像头的视频流(视频监控平台接收和转发的视频流)

目录 一、需求描述 二、解决思路 &#xff08;一&#xff09;问题分析 &#xff08;二&#xff09;解决思路 1、通过抓包的方式 2、通过一些linux的网络监视工具 三、需求实现 &#xff08;一&#xff09;抓包工具 1、tcpdump 2、Wireshark 3、tcptrace &#xff0…

【刷题】 二分查找进阶

送给大家一句话&#xff1a; 你向神求助是因为相信神&#xff0c;神没有回应你是因为神相信你 ε≡٩(๑>₃<)۶ &#xfeff;ε≡٩(๑>₃<)۶ &#xfeff;ε≡٩(๑>₃<)۶ 一心向学 二分查找进阶 1 前言Leetcode 852. 山脉数组的峰顶索引题目描述算法思…

【EtherCAT】FMMU和SM简介

目录 一、简介 1、 FMMU 2、SM (1) 缓冲模式 (2)邮箱模式 3、FMMU将物理存储器映射到逻辑过程数据映射的配置原理 二、FMMU和SM在EtherCAT从站控制器的存储空间分配 三、FMMU和SM部分寄存器描述(LAN9253) 1、FMMU 2、SM 四、FMMU和SM的数据结构&#xff08;soem主站&…

什么是AIoT?

什么是AIoT? AIoT&#xff0c;即人工智能物联网&#xff0c;是一种将人工智能&#xff08;AI&#xff09;技术与物联网&#xff08;IoT&#xff09;相结合的新型应用形态。它不仅实现了设备之间的互联互通&#xff0c;还赋予了它们更智能化的特性。AIoT的核心在于通过AI的数据…

什么是知乎知+广告推广?

知乎作为中国领先的知识分享社区和高质量用户群体汇聚地&#xff0c;其广告价值日益凸显&#xff0c;其中&#xff0c;“知”作为知乎官方推出的创新广告形式&#xff0c;正逐渐成为品牌与消费者深度连接的重要桥梁。知广告推广不仅局限于传统意义上的硬性推广&#xff0c;更强…

解锁棋盘之谜:探索N皇后问题的全方位解决策略【python 力扣51题】

作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 欢迎加入社区&#xff1a;码上找工作 作者专栏每日更新&#xff1a; LeetCode解锁1000题: 打怪升级之旅 python数据分析…

QML 中引用 js 文件闪退问题

问题描述 在移植 Android 中遇到这样一个引用兼容性问题&#xff0c;起因是这样的&#xff0c;Windows 版本的采用了 QML 分离的方式加载&#xff0c;而 Android 版本又采用了 qrc 的方式。而 Qt 中的机制是采用 QML 分离方式时则使用相对路径的方式引用 js 文件&#xff0c;而…

ZCMU 1531: 序列的混乱程度

Description 有一个长度为n的正整数序列&#xff0c;一个序列的混乱程度定义为这个序列的最大值和最小值之差。请编写一个程序&#xff0c;计算一个序列的混乱程度。 Input 输入的第一行为一个正整数T (T<1000)&#xff0c;表示一共有T组测试数据。 每组测试数据的第一行为一…

前后端跨域请求代码实战(vue3.4+springboot2.7.18)

前端代码 v3.4.21&#xff08;前端不是主业&#xff0c;所以就贴一贴代码&#xff0c;有疑问评论区见&#xff09;后端代码&#xff0c;springboot 2.7.18&#xff08;后端&#xff09; 文章内容&#xff1a; 一&#xff0c;后端代码 二&#xff0c;前端代码 三&#xff0c;后…