当今互联网项目,需求日渐增多,并且应用服务器的压力也日渐增大,这时就引入了分布式系统的概念,然后又有了动静分离,即动态资源与静态资源分开,使后端的应用服务器专注业务请求的处理,并降低因为请求静态资源而为应用服务器带来的压力。
FileSourceEnum
(用于后期实例化指定文件上传业务实现类)package com.qingyun.farm.enums;
import lombok.Getter;
@Getter
public enum FileSourceEnum {
LOCAL(1L,"LOCAL"),
ALIYUN(2L,"ALIYUN"),
FAST_DFS(3L,"FAST_DFS");
private Long code;
private String desc;
FileSourceEnum(Long code, String desc) {
this.code=code;
this.desc=desc;
}
}
FileServiceFactory
package com.qingyun.farm.factory;
import com.qingyun.farm.enums.FileSourceEnum;
import com.qingyun.farm.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.HashMap;
/**
* Created with IntelliJ IDEA.
* User: 李敷斌.
* Date: 2020-04-02
* Time: 11:20
* Explain: 文件业务工厂类
*/
@Component
public class FileServiceFactory {
private HashMap<FileSourceEnum, FileService> fileServiceMap=new HashMap<>();
@Autowired
private FileService localFileServiceImpl;
@Autowired
private FileService aliyunFileServiceImpl;
@Autowired
private FileService fastdfsFileServiceImpl;
@PostConstruct
private void initFileService(){
fileServiceMap.put(FileSourceEnum.LOCAL,localFileServiceImpl);
fileServiceMap.put(FileSourceEnum.ALIYUN,aliyunFileServiceImpl);
fileServiceMap.put(FileSourceEnum.FAST_DFS,fastdfsFileServiceImpl);
}
public FileService getFileService(Long fileSourceCode) {
if (fileSourceCode.equals(FileSourceEnum.FAST_DFS.getCode())){
return fileServiceMap.get(FileSourceEnum.FAST_DFS);
}else if (fileSourceCode.equals(FileSourceEnum.ALIYUN.getCode())){
return fileServiceMap.get(FileSourceEnum.ALIYUN);
}
return fileServiceMap.get(FileSourceEnum.LOCAL);
}
}
FileService
package com.qingyun.farm.service;
import java.io.IOException;
import com.qingyun.farm.model.FileInfo;
import org.springframework.web.multipart.MultipartFile;
public interface FileService {
FileInfo upload(MultipartFile file) throws Exception;
void delete(FileInfo fileInfo);
}
AbstractFileService
package com.qingyun.farm.service.impl;
import com.qingyun.farm.dao.FileInfoDao;
import com.qingyun.farm.enums.FileSourceEnum;
import com.qingyun.farm.model.FileInfo;
import com.qingyun.farm.service.FileService;
import com.qingyun.farm.utils.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
@Slf4j
public abstract class AbstractFileService implements FileService {
protected abstract FileInfoDao getFileDao();
@Override
public FileInfo upload(MultipartFile file) throws Exception {
FileInfo fileInfo = FileUtil.getFileInfo(file);
FileInfo oldFileInfo = getFileDao().getById(fileInfo.getId());
if (oldFileInfo != null) {
return oldFileInfo;
}
if (!fileInfo.getName().contains(".")) {
throw new IllegalArgumentException("缺少后缀名");
}
uploadFile(file, fileInfo);
fileInfo.setSource(fileSource().name());// 设置文件来源
getFileDao().save(fileInfo);// 将文件信息保存到数据库
log.info("上传文件:{}", fileInfo);
return fileInfo;
}
/**
* 文件来源
*
* @return
*/
protected abstract FileSourceEnum fileSource();
/**
* 上传文件
*
* @param file
* @param fileInfo
*/
protected abstract void uploadFile(MultipartFile file, FileInfo fileInfo) throws Exception;
@Override
public void delete(FileInfo fileInfo) {
deleteFile(fileInfo);
getFileDao().delete(fileInfo.getId());
log.info("删除文件:{}", fileInfo);
}
/**
* 删除文件资源
*
* @param fileInfo
* @return
*/
protected abstract boolean deleteFile(FileInfo fileInfo);
}
FileController
package com.qingyun.farm.controller;
import java.io.IOException;
import java.util.List;
import com.qingyun.farm.dao.FileInfoDao;
import com.qingyun.farm.dto.LayuiFile;
import com.qingyun.farm.factory.FileServiceFactory;
import com.qingyun.farm.model.FileInfo;
import com.qingyun.farm.page.table.PageTableHandler;
import com.qingyun.farm.page.table.PageTableRequest;
import com.qingyun.farm.page.table.PageTableResponse;
import com.qingyun.farm.service.FileService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.qingyun.farm.annotation.LogAnnotation;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@Api(tags = "文件")
@RestController
@RequestMapping("/files")
public class FileController {
@Autowired
private FileServiceFactory fileServiceFactory;
@Autowired
private FileInfoDao fileInfoDao;
@LogAnnotation
@PostMapping
@ApiOperation(value = "文件上传")
public FileInfo uploadFile(MultipartFile file,@RequestParam(value = "sourceCode",defaultValue = "1") Long sourceCode) throws IOException {
try {
return fileServiceFactory.getFileService(sourceCode).upload(file);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@LogAnnotation
@DeleteMapping("/{id}")
@ApiOperation(value = "文件删除")
@RequiresPermissions("sys:file:del")
public void delete(@PathVariable String id,Long sourceCode) {
FileInfo fileInfo = fileInfoDao.getById(id);
fileServiceFactory.getFileService(sourceCode).delete(fileInfo);
}
}
FileUtil
,用于获取上传文件的信息,以及具体实现本地文件上传。package com.qingyun.farm.utils;
import com.qingyun.farm.model.FileInfo;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.time.LocalDate;
import java.util.Date;
public class FileUtil {
public static FileInfo getFileInfo(MultipartFile file) throws Exception {
String md5 = fileMd5(file.getInputStream());
FileInfo fileInfo = new FileInfo();
fileInfo.setId(md5);// 将文件的md5设置为文件表的id
fileInfo.setName(file.getOriginalFilename());
fileInfo.setContentType(file.getContentType());
fileInfo.setIsImg(fileInfo.getContentType().startsWith("image/")?1L:0L);
fileInfo.setSize(file.getSize());
fileInfo.setCreateTime(new Date());
return fileInfo;
}
/**
* 文件的md5
*
* @param inputStream
* @return
*/
public static String fileMd5(InputStream inputStream) {
try {
return DigestUtils.md5Hex(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static String saveFile(MultipartFile file, String path) {
try {
File targetFile = new File(path);
if (targetFile.exists()) {
return path;
}
if (!targetFile.getParentFile().exists()) {
targetFile.getParentFile().mkdirs();
}
file.transferTo(targetFile);
return path;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static boolean deleteFile(String pathname) {
File file = new File(pathname);
if (file.exists()) {
boolean flag = file.delete();
if (flag) {
File[] files = file.getParentFile().listFiles();
if (files == null || files.length == 0) {
file.getParentFile().delete();
}
}
return flag;
}
return false;
}
public static String getPath() {
return "/" + LocalDate.now().toString().replace("-", "/") + "/";
}
/**
* 将文本写入文件
*
* @param value
* @param path
*/
public static void saveTextFile(String value, String path) {
FileWriter writer = null;
try {
File file = new File(path);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
writer = new FileWriter(file);
writer.write(value);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static String getText(String path) {
File file = new File(path);
if (!file.exists()) {
return null;
}
try {
return getText(new FileInputStream(file));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
public static String getText(InputStream inputStream) {
InputStreamReader isr = null;
BufferedReader bufferedReader = null;
try {
isr = new InputStreamReader(inputStream, "utf-8");
bufferedReader = new BufferedReader(isr);
StringBuilder builder = new StringBuilder();
String string;
while ((string = bufferedReader.readLine()) != null) {
string = string + "\n";
builder.append(string);
}
return builder.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (isr != null) {
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
以上就是文件上传业务实现的准备工作,可以根据具体业务修改相关代码实现。
package com.qingyun.farm.service.impl;
import java.io.IOException;
import java.time.LocalDate;
import com.qingyun.farm.enums.FileSourceEnum;
import com.qingyun.farm.model.FileInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.qingyun.farm.dao.FileInfoDao;
import com.qingyun.farm.service.FileService;
import com.qingyun.farm.utils.FileUtil;
@Service
public class LocalFileServiceImpl extends AbstractFileService {
@Autowired
private FileInfoDao fileInfoDao;
@Override
protected FileInfoDao getFileDao() {
return fileInfoDao;
}
@Value("${file.local.urlPrefix}")
private String urlPrefix;
/**
* 上传文件存储在本地的根路径
*/
@Value("${file.local.path}")
private String localFilePath;
@Override
protected FileSourceEnum fileSource() {
return FileSourceEnum.LOCAL;
}
@Override
protected void uploadFile(MultipartFile file, FileInfo fileInfo) throws Exception {
int index = fileInfo.getName().lastIndexOf(".");
// 文件扩展名
String fileSuffix = fileInfo.getName().substring(index);
String suffix = "/" + LocalDate.now().toString().replace("-", "/") + "/" + fileInfo.getId() + fileSuffix;
String path = localFilePath + suffix;
String url = urlPrefix + suffix;
fileInfo.setPath(path);
fileInfo.setUrl(url);
FileUtil.saveFile(file, path);
}
@Override
protected boolean deleteFile(FileInfo fileInfo) {
return FileUtil.deleteFile(fileInfo.getPath());
}
}
file:
local:
path: /Users/qingyun/IdeaProjects/SmartAgriculture/src/main/resources/static/upload
prefix: /upload
urlPrefix: http://localhost:8088/${file.local.prefix}
<dependency>
<groupId>org.csourcegroupId>
<artifactId>fastdfs-client-javaartifactId>
<version>1.29-SNAPSHOTversion>
dependency>
tracker.conf
tracker_server=192.168.69.139:22122
# 连接超时时间,针对socket套接字函数connect,默认为30秒
connect_timeout=30000
# 网络通讯超时时间,默认是60秒
network_timeout=60000
package com.qingyun.farm.config;
import org.csource.fastdfs.ClientGlobal;
import org.csource.fastdfs.StorageClient;
import org.csource.fastdfs.TrackerClient;
import org.csource.fastdfs.TrackerServer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Created with IntelliJ IDEA.
* User: 李敷斌.
* Date: 2020-02-26
* Time: 17:57
* Explain: 文件上传配置类
*/
@Configuration
@ConditionalOnProperty(prefix = "file.fastdfs.tracker.config",name = "path",havingValue = "/tracker.conf")
public class FastDFSUploadConfig {
@Value(value = "${file.fastdfs.tracker.config.path}")
private String trackerConfigPath;
@Bean
public StorageClient storageClient(){
TrackerClient trackerClient=null;
TrackerServer trackerServer=null;
try {
String tracker = FastDFSUploadConfig.class.getResource(trackerConfigPath).getPath();
//初始化
ClientGlobal.init(tracker);
//创建trackerClient
trackerClient=new TrackerClient();
//通过client获取service
trackerServer=trackerClient.getTrackerServer();
}catch (Exception e){
}
//以trackerservice为参数 构建storageclient
return new StorageClient(trackerServer,null);
}
}
package com.qingyun.farm.service.impl;
import com.qingyun.farm.dao.FileInfoDao;
import com.qingyun.farm.enums.FileSourceEnum;
import com.qingyun.farm.model.FileInfo;
import lombok.extern.slf4j.Slf4j;
import org.csource.common.MyException;
import org.csource.fastdfs.StorageClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* Created with IntelliJ IDEA.
* User: 李敷斌.
* Date: 2020-04-02
* Time: 11:58
* Explain: fastdfs文件上传
*/
@Service
@Slf4j
public class FastdfsFileServiceImpl extends AbstractFileService {
@Autowired
private StorageClient storageClient;
@Autowired
private FileInfoDao fileInfoDao;
@Value("${file.fastdfs.domain}")
private String fastdfs_domain;
@Override
protected FileInfoDao getFileDao() {
return fileInfoDao;
}
/**
* 文件来源
*
* @return
*/
@Override
protected FileSourceEnum fileSource() {
return FileSourceEnum.FAST_DFS;
}
/**
* 上传文件
*
* @param multipartFile
* @param fileInfo
*/
@Override
protected void uploadFile(MultipartFile multipartFile, FileInfo fileInfo) throws Exception {
//获取文件后缀名
String sourceFileName = multipartFile.getOriginalFilename();
//获取后缀名
String suffixName = sourceFileName.substring(sourceFileName.lastIndexOf(".")+1);
String[] uploadFileResult=null;
try {
//上传文件
uploadFileResult = storageClient.upload_file(multipartFile.getBytes(), suffixName, null);
} catch (Exception e){
log.error("【文件上传】获取二进制数据失败,ex.msg={}",e.getMessage());
}
String url="http://"+fastdfs_domain+getUrl(uploadFileResult);
fileInfo.setUrl(url);
log.debug("【文件上传】文件访问路径,imageUrl={}",url);
}
private String getUrl(String[] uploadFileResult){
StringBuffer sbf=new StringBuffer();
for (String item : uploadFileResult) {
sbf.append("/"+item);
}
return sbf.toString();
}
/**
* 删除文件资源
*
* @param fileInfo
* @return
*/
@Override
protected boolean deleteFile(FileInfo fileInfo) {
try {
return storageClient.delete_file("group1",fileInfo.getName())>0;
} catch (IOException e) {
e.printStackTrace();
} catch (MyException e) {
e.printStackTrace();
}
return false;
}
}
file:
fastdfs:
tracker:
config:
path: /tracker.conf
domain: 192.168.69.139
fastdfs文件上传至此以及完整地实现了。
<dependency>
<groupId>com.aliyun.ossgroupId>
<artifactId>aliyun-sdk-ossartifactId>
<version>${aliyun-sdk-oss.version}version>
dependency>
<dependency>
<groupId>com.aliyungroupId>
<artifactId>aliyun-java-sdk-coreartifactId>
<version>${aliyun-sdk-core.version}version>
dependency>
<dependency>
<groupId>com.aliyungroupId>
<artifactId>aliyun-java-sdk-dysmsapiartifactId>
<version>${aliyun-sdk-dysmsapi.version}version>
dependency>
对应版本信息:
<aliyun-sdk-oss.version>2.8.2aliyun-sdk-oss.version>
<aliyun-sdk-core.version>3.2.8aliyun-sdk-core.version>
<aliyun-sdk-dysmsapi.version>1.1.0aliyun-sdk-dysmsapi.version>
file:
aliyun:
endpoint: xxx
accessKeyId: xxx
accessKeySecret: xxx
bucketName: xxx
domain: xxx
package com.qingyun.farm.config;
import com.aliyun.oss.OSSClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
@Configuration
public class AliyunConfig {
@Value("${file.aliyun.endpoint}")
private String endpoint;
@Value("${file.aliyun.accessKeyId}")
private String accessKeyId;
@Value("${file.aliyun.accessKeySecret}")
private String accessKeySecret;
/**
* 阿里云文件存储client
*
*/
@Bean
public OSSClient ossClient() {
OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
return ossClient;
}
public static void main(String[] args) throws FileNotFoundException {
OSSClient ossClient = new OSSClient("oss-cn-beijing.aliyuncs.com", "LTAI3jTQMjLamd0v", "aOR1ZFUoJCKmiSUUQopZcwZDu0uei6");
InputStream inputStream = new FileInputStream("D://ssfw.sql");
ossClient.putObject("topwulian", "upload/" + "ss11fw.sql", inputStream);
Date expiration = new Date(new Date().getTime() + 3600l * 1000 * 24 * 365 * 10);
// 生成URL
URL url = ossClient.generatePresignedUrl("topwulian", "upload/" + "ss11fw.sql", expiration);
System.out.println(url);
}
}
package com.qingyun.farm.service.impl;
import com.aliyun.oss.OSSClient;
import com.qingyun.farm.dao.FileInfoDao;
import com.qingyun.farm.enums.FileSourceEnum;
import com.qingyun.farm.model.FileInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* 阿里云存储文件
*
* @author 小威老师
*
*/
@Service("aliyunFileServiceImpl")
public class AliyunFileServiceImpl extends AbstractFileService {
@Autowired
private FileInfoDao fileInfoDao;
@Override
protected FileInfoDao getFileDao() {
return fileInfoDao;
}
@Override
protected FileSourceEnum fileSource() {
return FileSourceEnum.ALIYUN;
}
@Autowired
private OSSClient ossClient;
@Value("${file.aliyun.bucketName}")
private String bucketName;
@Value("${file.aliyun.domain}")
private String domain;
@Override
protected void uploadFile(MultipartFile file, FileInfo fileInfo) throws Exception {
ossClient.putObject(bucketName, fileInfo.getName(), file.getInputStream());
fileInfo.setUrl(domain + "/" + fileInfo.getName());
}
@Override
protected boolean deleteFile(FileInfo fileInfo) {
ossClient.deleteObject(bucketName, fileInfo.getName());
return true;
}
}
至此三种文件上传方式整合完毕,如果文章对你有帮助的话请给我点个赞哦,也可以关注博主我将持续更新一些组件使用教程❤️
!