SpringBoot 如何優(yōu)雅的實現(xiàn)跨服務(wù)器上傳文件的示例
項目完整代碼鏈接:代碼鏈接
跨服務(wù)上傳文件示意圖

一、創(chuàng)建項目
- springboot:2.2.6
- JDK:1.8
由于資源有限,就用不同端口表示不同服務(wù)器了。
1.1 上傳文件的項目
首先idea快速搭建工具創(chuàng)建一個springboot項目,名字為fileupload,作為上傳文件的服務(wù)端。
選擇spring web模塊即可

配置相關(guān)參數(shù)
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/
添加坐標(biāo)依賴
跨服器上傳所需要的jar包坐標(biāo)
<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 創(chuàng)建fileuploadserver 保存文件服務(wù)器
創(chuàng)建一個jeex項目,項目名字為fileuploadserver,什么都不需要配置。然后再webapp目錄下創(chuàng)建一個uploads文件夾,與上面項目設(shè)置的文件保存地址一直,然后配置好tomcat環(huán)境啟動即可。記得把web.xml文件里面的配置信息刪掉
如下圖所示



記得改下Http和JMX的端口,免得和其他項目沖突了。

二、編寫服務(wù)器接收文件上傳代碼
編寫一個controller類,用與處理文件上傳
MultipartFile類用來保存上傳的文件數(shù)據(jù)
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("跨服務(wù)器上傳文件上傳");
String filename = upload.getOriginalFilename();
// 把文件的名稱設(shè)置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 創(chuàng)建客戶端的對象
Client client = Client.create();
// 和圖片服務(wù)器進(jìn)行連接
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 問題分析
正如上面寫的,只要我們關(guān)聯(lián)了服務(wù)器地址之后就可以直接通過put方法把文件上傳上去,這無疑是非常危險的行為。因為在上傳過程中并沒有進(jìn)行用戶校驗,那么如果被人知道了服務(wù)器保存圖片的路徑,甚至不需要知道準(zhǔn)確路徑,只要知道服務(wù)器ip地址就夠了,那么他就可以通過put方法無限量的進(jìn)行服務(wù)器上傳。
根據(jù)apache官方在2017公布的一個漏洞,如果開啟了put方法,那么就可以任意寫寫入文件到服務(wù)器。但是如果禁用了put方法,那么又有導(dǎo)致一些需要put方法的業(yè)務(wù)無法使用。
一個解決辦法就是修改tomcat的配置。修改在tomcat的/conf目錄下的web.xml。找到下面這段
把readonly設(shè)置成true。這樣就無法通過put往服務(wù)器中寫入文件了。
<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>
但是這樣一來,我們就無法通過上述方法來進(jìn)行跨服務(wù)器上傳了,因為文件服務(wù)器已經(jīng)禁止了通過put方法寫入文件。那么這種情況應(yīng)該怎么辦呢?
有一種思路就是把服務(wù)器接收到的文件上傳請求,通過HttpPost再把上傳的文件信息發(fā)送到文件服務(wù)器。由文件服務(wù)器自己處理是否接收保存文件。

3.2 修改項目fileupload的配置
添加HttpPost的相關(guān)坐標(biāo)依賴
<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 創(chuàng)建fileuploadserver1 項目
創(chuàng)建一個springboot項目,選擇如**fileload**項目一樣。

創(chuàng)建好之后在/resources/目錄下創(chuàng)建一個uploads文件夾,用作保存上傳文件的位置。(也可以根據(jù)自己實際需要,更改文件保存的位置)
配置相關(guān)參數(shù)
# 文件上傳位置 這里是路徑是相對于項目而言,可以根據(jù)實際情況更改 file.upload.save-path=/uploads/ #文件訪問路徑 file.upload.url=/uploads/** server.port=8888 #文件大小設(shè)置 spring.servlet.multipart.enabled=true spring.servlet.multipart.max-file-size=30MB spring.servlet.multipart.max-request-size=100MB
3.4 編寫服務(wù)器接收文件上傳代碼
用數(shù)組的形式接收MultipartFile參數(shù),實現(xiàn)多文件上傳。
把上傳的文件用MultipartEntityBuilder打包好之后,再用HttpPost發(fā)送到文件服務(wù)器。這里最好需要了解一些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("跨服務(wù)器上傳文件上傳");
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 編寫文件服務(wù)器接收代碼
接收的Controller
ResourceUtils.getURL("classpath:") 獲取當(dāng)前項目所在的路徑,最好別存在中文,可能會出錯
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 效果展示



到此這篇關(guān)于SpringBoot 如何優(yōu)雅的實現(xiàn)跨服務(wù)器上傳文件的示例的文章就介紹到這了,更多相關(guān)SpringBoot 跨服務(wù)器上傳文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于json解析多層嵌套并轉(zhuǎn)為對應(yīng)類(List)
在進(jìn)行JSON解析時,遇到多層嵌套結(jié)構(gòu)可通過遞歸或?qū)S脦靵韺崿F(xiàn),重要的是將嵌套的JSON對象準(zhǔn)確轉(zhuǎn)化為對應(yīng)的Java類,通常需要依賴如Gson或Jackson等庫,將JSONObject轉(zhuǎn)為JavaBean時,關(guān)注字段匹配與數(shù)據(jù)類型轉(zhuǎn)換2024-10-10
Mybatis mapper標(biāo)簽中配置子標(biāo)簽package的坑及解決
這篇文章主要介紹了Mybatis mapper標(biāo)簽中配置子標(biāo)簽package的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
SpringBoot+Security 發(fā)送短信驗證碼的實現(xiàn)
這篇文章主要介紹了SpringBoot+Security 發(fā)送短信驗證碼的實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
java構(gòu)建Stream流的多種方式總結(jié)
Java?8引入了Stream流作為一項新的特性,它是用來處理集合數(shù)據(jù)的一種函數(shù)式編程方式,本文為大家整理了多種java構(gòu)建Stream流的方式,希望對大家有所幫助2023-11-11

