SpringBoot 如何优雅的实现跨服务器上传文件的示例
作者:jiangxiaoju 发布时间:2023-07-03 21:42:56
项目完整代码链接:代码链接
跨服务上传文件示意图
一、创建项目
springboot:2.2.6
JDK:1.8
由于资源有限,就用不同端口表示不同服务器了。
1.1 上传文件的项目
首先idea快速搭建工具创建一个springboot项目,名字为fileupload
,作为上传文件的服务端。
选择spring web模块即可
配置相关参数
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=30MB
spring.servlet.multipart.max-request-size=30MB
#文件保存的url,末尾的 / 别漏了
file.upload.path=http://localhost:8888/fileuploadserver/uploads/
添加坐标依赖
跨服器上传所需要的jar包坐标
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.18.1</version>
</dependency>
1.2 创建fileuploadserver 保存文件服务器
创建一个jeex项目,项目名字为fileuploadserver
,什么都不需要配置。然后再webapp目录下创建一个uploads
文件夹,与上面项目设置的文件保存地址一直,然后配置好tomcat环境启动即可。记得把web.xml文件里面的配置信息删掉
如下图所示
记得改下Http和JMX的端口,免得和其他项目冲突了。
二、编写服务器接收文件上传代码
编写一个controller类,用与处理文件上传
MultipartFile类用来保存上传的文件数据
package cn.jxj4869.fileupload.controller;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.UUID;
@Controller
public class FileController {
@Value("${file.upload.path}")
private String path;
@RequestMapping("/fileupload/method1")
@ResponseBody
private String method1(@RequestParam("upload") MultipartFile upload) throws IOException {
System.out.println("跨服务器上传文件上传");
String filename = upload.getOriginalFilename();
// 把文件的名称设置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 创建客户端的对象
Client client = Client.create();
// 和图片服务器进行连接
WebResource webResource = client.resource(path + filename);
webResource.put(upload.getBytes());
return "success";
}
}
前端代码
放在/resources/static/
目录下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>fileupload</title>
</head>
<body>
<h3>method1</h3>
<form method="post" enctype="multipart/form-data" action="fileupload/method1">
<input type="file" name="upload">
<br>
<input type="submit">
</form>
</body>
</html>
上传效果如下
三、分析存在问题以及解决办法
3.1 问题分析
正如上面写的,只要我们关联了服务器地址之后就可以直接通过put
方法把文件上传上去,这无疑是非常危险的行为。因为在上传过程中并没有进行用户校验,那么如果被人知道了服务器保存图片的路径,甚至不需要知道准确路径,只要知道服务器ip地址就够了,那么他就可以通过put方法无限量的进行服务器上传。
根据apache官方在2017公布的一个漏洞,如果开启了put方法,那么就可以任意写写入文件到服务器。但是如果禁用了put
方法,那么又有导致一些需要put
方法的业务无法使用。
一个解决办法就是修改tomcat的配置。修改在tomcat的/conf目录下的web.xml
。找到下面这段
把readonly
设置成true。这样就无法通过put
往服务器中写入文件了。
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>readonly</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
但是这样一来,我们就无法通过上述方法来进行跨服务器上传了,因为文件服务器已经禁止了通过put
方法写入文件。那么这种情况应该怎么办呢?
有一种思路就是把服务器接收到的文件上传请求,通过HttpPost再把上传的文件信息发送到文件服务器。由文件服务器自己处理是否接收保存文件。
3.2 修改项目fileupload的配置
添加HttpPost的相关坐标依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.1</version>
</dependency>
添加配置:
application.properties
file.upload.path1=http://localhost:8888/fileupload/
3.3 创建fileuploadserver1 项目
创建一个springboot项目,选择如**fileload
**项目一样。
创建好之后在/resources/
目录下创建一个uploads
文件夹,用作保存上传文件的位置。(也可以根据自己实际需要,更改文件保存的位置)
配置相关参数
# 文件上传位置 这里是路径是相对于项目而言,可以根据实际情况更改
file.upload.save-path=/uploads/
#文件访问路径
file.upload.url=/uploads/**
server.port=8888
#文件大小设置
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=30MB
spring.servlet.multipart.max-request-size=100MB
3.4 编写服务器接收文件上传代码
用数组的形式接收MultipartFile
参数,实现多文件上传。
把上传的文件用MultipartEntityBuilder
打包好之后,再用HttpPost发送到文件服务器。这里最好需要了解一些HttpPost
用法。
package cn.jxj4869.fileupload.controller;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.UUID;
@Controller
public class FileController {
@Value("${file.upload.path1}")
private String path1;
@RequestMapping("/fileupload/method2")
@ResponseBody
private String method2(@RequestParam("upload") MultipartFile[] uploads) throws IOException {
System.out.println("跨服务器上传文件上传");
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(path1);
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
for (MultipartFile upload : uploads) {
String filename = upload.getOriginalFilename();
builder.addBinaryBody("upload", upload.getBytes(), ContentType.MULTIPART_FORM_DATA, filename);
}
try {
HttpEntity entity = builder.build();
httpPost.setEntity(entity);
CloseableHttpResponse response = httpClient.execute(httpPost);
System.out.println(response.getStatusLine().getStatusCode());
String s = response.getEntity().toString();
System.out.println(s);
} catch (Exception e) {
} finally {
httpClient.close();
}
return "success";
}
}
前端部分的代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>fileupload</title>
</head>
<body>
<h3>method2</h3>
<form method="post" enctype="multipart/form-data" action="fileupload/method2">
<input type="file" name="upload"><br><br>
<input type="file" name="upload"><br><br>
<input type="file" name="upload">
<br><br>
<input type="submit">
</form>
</body>
</html>
3.5 编写文件服务器接收代码
接收的Controller
ResourceUtils.getURL("classpath:")
获取当前项目所在的路径,最好别存在中文,可能会出错
package cn.jxj4869.fileuploadserver1.controller;
import com.sun.javafx.scene.shape.PathUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
@Controller
public class FileController {
@Value("${file.upload.save-path}")
private String savePath;
@PostMapping("/fileupload")
@ResponseBody
private String fileupload(HttpServletRequest request, @RequestParam("upload")MultipartFile[] uploads) throws IOException {
System.out.println("文件上传");
String path= ResourceUtils.getURL("classpath:").getPath()+savePath;
File file = new File(path);
if (!file.exists()) {
file.mkdir();
}
for (MultipartFile upload : uploads) {
String filename = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replace("-", "");
filename=uuid+"_"+filename;
upload.transferTo(new File(path,filename));
}
return "success";
}
}
编写配置类
package cn.jxj4869.fileuploadserver1.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cglib.core.WeakCacheKey;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MySpringMvcConfig implements WebMvcConfigurer {
@Value("${file.upload.save-path}")
private String savePath;
@Value("${file.upload.url}")
private String url;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(url).addResourceLocations("classpath:"+savePath);
}
}
3.6 效果展示


猜你喜欢
- 如下图所示,你的UI元素可能小于48dp,图标仅有32dp,按钮仅有40dp,但是他们的实际可操作焦点区域最好都应达到48dp的大小。为使小
- 项目中最近用到各种图表,本来打算用第三方的,例如MPAndroid,这是一个十分强大的图表库,应用起来十分方便,但是最终发现和设计不太一样,
- Java线程分为两类分别为daemon线程(守护线程)和User线程(用户线程),在JVM启动时候会调用main函数,main函数所在的线程
- //创建excelobject missing = System.Reflection.Missing.Value;Excel.Applic
- java8的stream取max public static void main(String[] args) { &
- 问题描述平常用的是java8,最近在学习java的新特性。这就需要从java8往更高的java版本切换。由于还在使用java8,测试完新特性
- CLR支持两种类型:引用类型和值类型。 引用类型总是从托管堆上分配的。 c#中的New操作符返回对象的内存地址。 引用对象的注意点: 1、内
- 目录(1)class常量池(2)运行时常量池(3)基本类型包装类常量池(4)字符串常量池总结java中有几种不同的常量池,以下的内容是对ja
- 开发中对版本进行检查并更新的需求基本是所有应用必须有的功能,可是在实际开发中有些朋友就容易忽略一些细节。版本更新的基本流程:一般是将本地版本
- 这篇文章主要介绍了JavaWeb如何实现禁用浏览器缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- 前言这是该工具的github地址:https://github.com/pingfangushi/screw一、引入pom.xml依赖<
- 普通的excel列表,easyexcel读取是没有什么问题的。但是,如果有合并单元格,那么它读取的时候,能获取数
- springmvc控制登录用户session失效后跳转登录页面,废话不多少了,具体如下:第一步,配置 web.xml <session
- 写在前面最近有很多的粉丝私信我,说自己在面试的时候,老是被人问HashMap的原理,但是在实际的工作中,也只是使用HashMap,从来就没有
- 一、自动装配1、四种类型的自动装配类型解释xml 配置byName根据 Bean 的 name 或者 id<bean id=”bean
- 本文实例讲述了Android编程ProgressBar自定义样式之动画模式实现方法。分享给大家供大家参考,具体如下:忘记在哪里看到的那位仁兄
- @CompentScan excludeFilters配置无效@CompentScan 注解配置需要扫描的包excludeFilters 是
- 大多数网站会设置用户权限,如过滤非法用户,用户不登录时不能进行访问,或者设置访问的权限,如部分内容仅对VIP开放等等,这些权限的控制都可以用
- 当我们打开app的时候是不是会有一瞬间的白屏然后再进入主活动,虽然这并不会造成什么不好的后果,但是感觉用户体验就不是很好。像网易云音乐等等
- try就像一个网,把try{}里面的代码所抛出的异常都网住,然后把异常交给catch{}里面的代码去处理。最后执行finally之中的代码。