SpringBoot 跨服务器上传文件 SpringBoot 怎样优雅的实现跨服务器上传文件的示例
jiangxiaoju 人气:0项目完整代码链接:代码链接
跨服务上传文件示意图
一、创建项目
- 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 效果展示
加载全部内容