前言
最近試着搭建一個文件服務器,因爲在企業應用開發中文件往往都是單獨存儲在一個服務器中的,與應用服務器相分離,數據庫層面引用該文件只需要保存一個文件的url即可;
大致流程就是,前端請求文件服務器接口,接口邏輯處理保存該文件到服務器中,並返回可以訪問該文件的url給前端;
技術棧
後端Java,SpringBoot2.2.2.RELEASE
服務器Centos7,Nginx
後端處理詳情
- 分環境開發部署,開發環境和測試環境,因爲開發環境下使用的是Windows系統,文件路徑與Linux系統不太一樣,而且記錄日誌的方式也不一樣,開發環境下日誌我就直接輸出在控制檯,生產環境下日誌記錄到文件,所以利用Maven的profiles部署兩個環境
<profiles>
<profile>
<!-- 開發環境 -->
<id>dev</id>
<properties>
<profiles.active>dev</profiles.active>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<!-- 生產環境 -->
<id>pro</id>
<properties>
<profiles.active>pro</profiles.active>
</properties>
</profile>
</profiles>
分環境的不同配置,日誌和存儲路徑
spring:
#指定當前的環境------在pom文件中取值使用@profiles.active@
profiles:
active: @profiles.active@
#日誌配置文件位置
logging:
config: classpath:log4j/${spring.profiles.active}/log4j2.xml
---
spring:
profiles: dev
# 開發環境下第一層文件存儲目錄,windows下文檔路徑 / 和 \ 都可以
filePath: K:/forFileUpload/
---
spring:
profiles: pro
# 生產環境下第一層文件存儲目錄
filePath: /fei/fileupload/
不同環境的日誌文件
- 避免文件名重複,使用UUID重命名文件名;根據日期每天生成一個次級文件夾;這都是具體接口的處理邏輯
package com.fei.fileupload.web;
import com.fei.common.data.ApiResult;
import com.fei.common.log.Loggable;
import com.fei.fileupload.model.FileModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.time.LocalDate;
import java.util.UUID;
/**
* @Author: xiaoshijiu
* @Date: 2019/12/19
* @Description: 搭建文件服務器,接收文件入口
*/
@RestController
@RequestMapping("/file")
public class FileUpload implements Loggable {
@Value("${filePath}")
private String filePath;
@Value("${spring.profiles:dev}")
private String profiles;
@Value("${serverAddress}")
private String serverAddress;
@PostMapping("/upload")
public ApiResult<FileModel> getFile(MultipartFile file) throws IOException {
if (file.isEmpty()) {
return ApiResult.fail("失敗,文件不存在!!");
}
getLog().info("文件的大小是:{}", file.getSize());
getLog().info("文件的類型是:{}", file.getContentType());
String fileName = file.getOriginalFilename();
getLog().info("文件的名稱是:{}", fileName);
// 文件名處理(避免文件重名覆蓋),使用UUID
String exname = fileName.substring(fileName.lastIndexOf("."));
fileName = UUID.randomUUID().toString().replace("-", "") + exname;
// 每天一個文件夾,第二層目錄
String fileDatePath = LocalDate.now().toString();
String DateFileName = fileDatePath + "/" + fileName;
String endFilePath = filePath + DateFileName;
File fileTotlePath = new File(filePath + fileDatePath);
// 總路徑不存在,新建(這裏路徑必須要確保存在,以免下面轉換的時候報FileNotFoundException)
if (!fileTotlePath.exists()) {
fileTotlePath.mkdirs();
}
// 將file轉換到指定目錄
file.transferTo(new File(endFilePath));
FileModel fileModel;
if ("dev".equals(profiles)) {
fileModel = new FileModel(endFilePath);
getLog().info("文件最終url是:{}", endFilePath);
} else {
fileModel = new FileModel(serverAddress + DateFileName);
getLog().info("文件最終url是:{}", serverAddress + DateFileName);
}
return ApiResult.ok(fileModel);
}
}
- 新版SpringBoot配置接受的最大文件大小(跟之前版本不太一樣,有一個新的類
DataSize
)
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Value("${fileMaxSize:10}")
private Long fileMaxSize;
/**
* 接受文件大小配置
*/
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
// 文件最大
// DataSize.ofMegabytes(10L),10MB
factory.setMaxFileSize(DataSize.ofMegabytes(fileMaxSize));
// 設置總上傳數據總大小
factory.setMaxRequestSize(DataSize.ofMegabytes(fileMaxSize));
return factory.createMultipartConfig();
}
}
- 攔截器過濾ip,給指定的ip放行訪問;因爲有的公司搭建的文件服務器,可能只是爲了供公司內網使用,這時候我們就需要對請求的客戶端ip進行過濾
package com.fei.fileupload.interceptor;
import com.fei.common.exception.NotAuthException;
import com.fei.common.server.iputil.GetClientIp;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Author: xiaoshijiu
* @Date: 2019/12/20
* @Description: ip攔截器,文件服務器只供內部使用,所以需要攔截ip。選擇性放行
*/
public class IpInteceptor implements HandlerInterceptor {
@Value("${permissionURL}")
private String permissionURL;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// 獲取ip
String ipAdrress = GetClientIp.getIpAdrress(request);
if (ipAdrress.contains(permissionURL)) {
return true;
}
// 拋出去一個異常,供捕獲返回
throw new NotAuthException("對不起你沒有權限,不允許訪問!!");
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
}
}
攔截器配置,注意這裏配置攔截器的時候:最好要以 getIpInterceptor() 和 @Bean 的形式配置攔截器 不然在攔截器裏面不能使用 @value 取值
因爲@Bean 的形式,spring管理bean,能夠依賴注入
package com.fei.fileupload.config;
import com.fei.fileupload.interceptor.IpInteceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.unit.DataSize;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.servlet.MultipartConfigElement;
/**
* @Author: xiaoshijiu
* @Date: 2019/12/19
* @Description: Web的一些配置
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public IpInteceptor getIpInterceptor() {
return new IpInteceptor();
}
/**
* 注意事項:這裏最好要以 getIpInterceptor() 和 @Bean 的形式配置攔截器 不然在攔截器裏面不能使用 @value 取值
* @Bean 的形式,spring管理bean,能夠依賴注入
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加攔截器,指定攔截請求和排除請求
registry.addInterceptor(getIpInterceptor()).addPathPatterns("/**")
.excludePathPatterns("/error");
}
}
- 大致後端情況就這麼多,還有一些其他的像什麼全局異常處理Controller等啊,就不需要介紹了
服務器Nginx配置
網上也有些人使用的是Tomcat加設置虛擬目錄的形式,達到可以在線訪問指定目錄的效果;但是在處理靜態資源這一塊,Nginx比Tomcat效率要高很多,也是第一選擇;
安裝好Nginx,在/usr/local/nginx/conf裏面找到nginx.conf
加上一組location,進行映射,訪問ip:port/file/xx.jpg
就相當於訪問/fei/fileupload/
下面的xx.jpg
location /file/ {
alias /fei/fileupload/;
}
最後
至此,文件服務器搭建完畢,前端請求Java的接口,經過處理保存到指定目錄,並返回可以訪問的url;