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

一、創(chuàng)建項(xiàng)目
- springboot:2.2.6
- JDK:1.8
由于資源有限,就用不同端口表示不同服務(wù)器了。
1.1 上傳文件的項(xiàng)目
首先idea快速搭建工具創(chuàng)建一個(gè)springboot項(xiàng)目,名字為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)依賴(lài)
跨服器上傳所需要的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)建一個(gè)jeex項(xiàng)目,項(xiàng)目名字為fileuploadserver,什么都不需要配置。然后再webapp目錄下創(chuàng)建一個(gè)uploads文件夾,與上面項(xiàng)目設(shè)置的文件保存地址一直,然后配置好tomcat環(huán)境啟動(dòng)即可。記得把web.xml文件里面的配置信息刪掉
如下圖所示



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

二、編寫(xiě)服務(wù)器接收文件上傳代碼
編寫(xiě)一個(gè)controller類(lèi),用與處理文件上傳
MultipartFile類(lèi)用來(lái)保存上傳的文件數(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();
// 把文件的名稱(chēng)設(shè)置唯一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 創(chuàng)建客戶(hù)端的對(duì)象
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>
上傳效果如下




三、分析存在問(wèn)題以及解決辦法
3.1 問(wèn)題分析
正如上面寫(xiě)的,只要我們關(guān)聯(lián)了服務(wù)器地址之后就可以直接通過(guò)put方法把文件上傳上去,這無(wú)疑是非常危險(xiǎn)的行為。因?yàn)樵谏蟼鬟^(guò)程中并沒(méi)有進(jìn)行用戶(hù)校驗(yàn),那么如果被人知道了服務(wù)器保存圖片的路徑,甚至不需要知道準(zhǔn)確路徑,只要知道服務(wù)器ip地址就夠了,那么他就可以通過(guò)put方法無(wú)限量的進(jìn)行服務(wù)器上傳。
根據(jù)apache官方在2017公布的一個(gè)漏洞,如果開(kāi)啟了put方法,那么就可以任意寫(xiě)寫(xiě)入文件到服務(wù)器。但是如果禁用了put方法,那么又有導(dǎo)致一些需要put方法的業(yè)務(wù)無(wú)法使用。
一個(gè)解決辦法就是修改tomcat的配置。修改在tomcat的/conf目錄下的web.xml。找到下面這段
把readonly設(shè)置成true。這樣就無(wú)法通過(guò)put往服務(wù)器中寫(xiě)入文件了。
<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>
但是這樣一來(lái),我們就無(wú)法通過(guò)上述方法來(lái)進(jìn)行跨服務(wù)器上傳了,因?yàn)槲募?wù)器已經(jīng)禁止了通過(guò)put方法寫(xiě)入文件。那么這種情況應(yīng)該怎么辦呢?
有一種思路就是把服務(wù)器接收到的文件上傳請(qǐng)求,通過(guò)HttpPost再把上傳的文件信息發(fā)送到文件服務(wù)器。由文件服務(wù)器自己處理是否接收保存文件。

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

創(chuàng)建好之后在/resources/目錄下創(chuàng)建一個(gè)uploads文件夾,用作保存上傳文件的位置。(也可以根據(jù)自己實(shí)際需要,更改文件保存的位置)
配置相關(guān)參數(shù)
# 文件上傳位置 這里是路徑是相對(duì)于項(xiàng)目而言,可以根據(jù)實(shí)際情況更改 file.upload.save-path=/uploads/ #文件訪問(wèn)路徑 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 編寫(xiě)服務(wù)器接收文件上傳代碼
用數(shù)組的形式接收MultipartFile參數(shù),實(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 編寫(xiě)文件服務(wù)器接收代碼
接收的Controller
ResourceUtils.getURL("classpath:") 獲取當(dāng)前項(xiàng)目所在的路徑,最好別存在中文,可能會(huì)出錯(cuò)
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";
}
}
編寫(xiě)配置類(lèi)
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)雅的實(shí)現(xiàn)跨服務(wù)器上傳文件的示例的文章就介紹到這了,更多相關(guān)SpringBoot 跨服務(wù)器上傳文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis如何傳入多個(gè)參數(shù)的實(shí)現(xiàn)代碼
這篇文章主要介紹了Mybatis如何傳入多個(gè)參數(shù)的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
淺談System.getenv()和System.getProperty()的區(qū)別
這篇文章主要介紹了System.getenv()和System.getProperty()的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
解決SpringMvc后臺(tái)接收json數(shù)據(jù)中文亂碼問(wèn)題的幾種方法
本篇文章主要介紹了解決SpringMvc后臺(tái)接收json數(shù)據(jù)中文亂碼問(wèn)題的幾種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
最值得Java開(kāi)發(fā)者收藏的網(wǎng)站
這篇文章主要為大家分享了最值得Java開(kāi)發(fā)者收藏的11個(gè)網(wǎng)站,幫助Java開(kāi)發(fā)者提升編程能力2016-11-11
MyBatis多表查詢(xún)和注解開(kāi)發(fā)案例詳解
這篇文章主要介紹了MyBatis多表查詢(xún)和注解開(kāi)發(fā),本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05
Java網(wǎng)絡(luò)編程教程之設(shè)置請(qǐng)求超時(shí)的方法
這篇文章主要給大家介紹了關(guān)于Java網(wǎng)絡(luò)編程教程之設(shè)置請(qǐng)求超時(shí)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12
spring boot aop 記錄方法執(zhí)行時(shí)間代碼示例
這篇文章主要介紹了spring boot aop 記錄方法執(zhí)行時(shí)間代碼示例,分享了相關(guān)代碼,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-02-02

