spring cloud feign 文件上传和文件下载
文件上传参考文档:http://blog.didispace.com/spring-cloud-starter-dalston-2-4/
文件下载参考文档:https://blog.csdn.net/aaronsimon/article/details/82710979
我的spring boot ,spring cloud 版本是:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.M9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
服务调用方 feign文件下载需要配置的config:
1 2 3 4 5 6 7 8 9 10 11 12 | import feign.codec.Encoder; import feign.form.spring.SpringFormEncoder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeignMultipartSupportConfig { @Bean public Encoder feignFormEncoder() { return new SpringFormEncoder(); } } |
对应的feign pom.xml需要引入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <!--feign upload file--> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version> 3.0 . 3 </version> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version> 3.0 . 3 </version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version> 1.3 . 3 </version> </dependency> |
服务调用方controller:
import com.yft.common.annotation.Log; import com.yft.sys.modules.test.fileclient.FileTestClient; import feign.Response; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; /** * feign 熔断器示例 * * @author oKong */ @RestController @Slf4j public class FileController { @Autowired FileTestClient fileTestClient; @Log("文件上传测试") @PostMapping("/upload") public Object upload(MultipartFile file) { log.info("使用feign调用服务,文件上传"); return fileTestClient.upload(file); } @Log("文件下载测试") @RequestMapping(value = "/download", method = RequestMethod.GET) public ResponseEntity<byte[]> downFile() { log.info("使用feign调用服务 文件下载"); ResponseEntity<byte[]> result = null; InputStream inputStream = null; try { // feign文件下载 Response response = fileTestClient.download(); Response.Body body = response.body(); inputStream = body.asInputStream(); byte[] b = new byte[inputStream.available()]; inputStream.read(b); HttpHeaders heads = new HttpHeaders(); heads.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=lr.xls"); heads.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); result = new ResponseEntity<byte[]>(b, heads, HttpStatus.OK); } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return result; } }
服务调用方feign client写法(文件上传主要是上面第一步配置,文件下载主要是返回的feign 的response):
import com.yft.sys.modules.ClientUrl; import com.yft.sys.modules.test.fileclient.impl.FileTestClientFallbackFactory; import feign.Response; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.multipart.MultipartFile; /** * @author lr */ @FeignClient(name = ClientUrl.SYSTEM_NAME, fallbackFactory = FileTestClientFallbackFactory.class) @Component public interface FileTestClient { /** * 上传文件测试 * * @return */ @PostMapping(value = ClientUrl.PRE_REQUEST_RUL + "/file/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) Object upload(MultipartFile file); /** * 下载文件测试 */ @RequestMapping(value = ClientUrl.PRE_REQUEST_RUL + "/file/download", method = RequestMethod.GET) Response download(); }
服务调用方 feign client 异常类:
import com.yft.sys.modules.test.fileclient.FileTestClient; import feign.Response; import feign.hystrix.FallbackFactory; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; /** * @author lr */ @Slf4j @Component public class FileTestClientFallbackFactory implements FallbackFactory<FileTestClient> { @Override public FileTestClient create(Throwable cause) { return new FileTestClient() { @Override public Object upload(MultipartFile file) { log.error("fallback; file upload reason was: " + cause.getMessage()); return null; } @Override public Response download() { log.error("fallback; file download reason was: " + cause.getMessage()); return null; } }; } }
服务提供方呢与原来写法一样,没差别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | @PostMapping (value = "upload" , consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public R upload(MultipartFile file) { try { String fileName = file.getOriginalFilename(); fileName = FileUtils.renameToUUID(fileName); String resPath = FileUtils.saveFile(file.getBytes(), filePath, fileName); // fileService.save(new FileDO() {{ // setCreateDate(new Date()); // setUrl("http://localhost:8004" + filePre + "/"+resPath); // setType(1); // }}); return R.ok().put( "resPath" , resPath); } catch (IOException e) { e.printStackTrace(); return R.error( "文件上传失败" ); } } @GetMapping ( "/download" ) public void downLoad(HttpServletResponse response) throws IOException {<br> //这里示意下载excel文件,自己随便下载点东西<br> /*我们先定义一个嵌套的List,List的元素也是一个List,内层的一个List代表一行数据, 每行都有4个单元格,最终list对象代表多行数据。*/ List<String> row1 = CollUtil.newArrayList( "aa" , "bb" , "cc" , "dd" ); List<String> row2 = CollUtil.newArrayList( "aa1" , "bb1" , "cc1" , "dd1" ); List<String> row3 = CollUtil.newArrayList( "aa2" , "bb2" , "cc2" , "dd2" ); List<String> row4 = CollUtil.newArrayList( "aa3" , "bb3" , "cc3" , "dd3" ); List<String> row5 = CollUtil.newArrayList( "aa4" , "bb4" , "cc4" , "dd4" ); List<List<String>> rows = CollUtil.newArrayList(row1, row2, row3, row4, row5); // 然后我们创建ExcelWriter对象后写出数据: // 通过工具类创建writer,默认创建xls格式 ExcelWriter writer = ExcelUtil.getWriter(); // 一次性写出内容,使用默认样式 writer.write(rows); //out为OutputStream,需要写出到的目标流 //response为HttpServletResponse对象 response.setContentType( "application/vnd.ms-excel;charset=utf-8" ); //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 response.setHeader( "Content-Disposition" , "attachment;filename=test.xls" ); ServletOutputStream out = response.getOutputStream(); writer.flush(out); // 关闭writer,释放内存 writer.close(); } |
文件上传配置yml
#上传文件配置
app:
filePath: D:/uploaded_files/
pre: /files
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Value("${app.filePath}")
String filePath;
@Value("${app.pre}")
String pre;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(pre + "/**").addResourceLocations("file:///" + filePath);
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.UUID; public class FileUtils { public static String saveFile( byte [] file, String filePath, String fileName) { int random = ( int ) (Math.random() * 100 + 1 ); int random1 = ( int ) (Math.random() * 100 + 1 ); filePath = filePath + random + File.separator + random1 + File.separator; File targetFile = new File(filePath); if (!targetFile.exists()) { targetFile.mkdirs(); } FileOutputStream fileOutputStream = null ; try { fileOutputStream = new FileOutputStream(filePath + fileName); FileChannel fileChannel = fileOutputStream.getChannel(); ByteBuffer buf = ByteBuffer.wrap(file); while (fileChannel.write(buf) != 0 ) { } } catch (Exception e) { } finally { if (fileOutputStream != null ) { try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } //url return random + "/" + random1 + "/" + fileName; } public static boolean deleteFile(String fileName) { File file = new File(fileName); // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除 if (file.exists() && file.isFile()) { if (file.delete()) { return true ; } else { return false ; } } else { return false ; } } public static String renameToUUID(String fileName) { return UUID.randomUUID() + "." + fileName.substring(fileName.lastIndexOf( "." ) + 1 ); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)