一、需求:
写一个统一文件下载接口,文件存储的位置有minio、monggoDb、磁盘、oss…等等
二、实现方式:
采用策略模式,根据不同的文件存储类型,选择不同的策略,输出文件流,实现文件下载。
大概结构如下:
三、实现过程:
实体类:
public class FileDownloadInputDto implements Serializable{
/**
* @Fields serialVersionUID : TODO
*/
private static final long serialVersionUID = 1L;
/**
* @Fields fileId : 文件编号
*/
private UUID fileId;
/**
* @Fields outputStream : 文件输入流
*/
private OutputStream outputStream;
/**
* @Fields storageType : 文件存储类型 hky add 2022.5.26
*/
private String storageType;
}
1.编写策略接口
import com.neuxa.springbreeze.document.dto.FileDownloadInputDto;
/**
* 存储策略接口</br>
*
* @author syp1293
* @since V1.0.0<br>
* 2022年5月27日 下午4:09:44</br>
*/
public interface StorageStrategy {
void downloadFile(FileDownloadInputDto dto);
}
2.策略实现类
2.1 minio下载实现
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;
import com.neuxa.springbreeze.document.domain.service.MinioFileManageService;
import com.neuxa.springbreeze.document.domain.service.StorageStrategy;
import com.neuxa.springbreeze.document.dto.FileDownloadInputDto;
/**
*
* minio策略类</br>
*
* @author syp1293
* @since V1.0.0<br>
* 2022年5月26日 下午2:17:30</br>
*/
@Service("minioStorageStrategy")
public class MinioStorageStrategyImpl implements StorageStrategy {
/**
* minioFileManageService minio类注入
*/
@Autowired
private MinioFileManageService minioFileManageService;
/**
* minio下载文件
*/
@Override
public void downloadFile(FileDownloadInputDto dto) {
InputStream is = minioFileManageService.downloadFile(dto.getFileId().toString());
OutputStream os = dto.getOutputStream();
try {
StreamUtils.copy(is, os);
os.flush();
is.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (os != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.2 monggoDb下载实现
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.neuxa.springbreeze.document.domain.service.StorageStrategy;
import com.neuxa.springbreeze.document.dto.FileDownloadInputDto;
import com.neuxa.springbreeze.document.util.MongoDbConvert;
import com.neuxa.springbreeze.document.util.SbzMongoDbUtils;
/**
*
* monggoDb策略类</br>
*
* @author syp1293
* @since V1.0.0<br>
* 2022年5月26日 下午2:20:29</br>
*/
@Service("monggoStorageStrategy")
public class MonggoStorageStrategyImpl implements StorageStrategy {
/**
* @Fields defaultDatabase : 默认库
*/
@Value("${spring.mongo.database}")
private String defaultDatabase;
/**
* monggoDb下载文件
*/
@Override
public void downloadFile(FileDownloadInputDto dto) {
SbzMongoDbUtils.downloadFile(defaultDatabase, MongoDbConvert.uuidToMongostring(dto.getFileId()),
dto.getOutputStream());
}
}
3.上下文类
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.neuxa.springbreeze.document.constant.StorageTypeEnum;
import com.neuxa.springbreeze.document.domain.service.StorageStrategy;
import com.neuxa.springbreeze.document.dto.FileDownloadInputDto;
/**
*
* 存储策略上下文类</br>
*
* @author syp1293
* @since V1.0.0<br>
* 2022年5月26日 下午2:08:29</br>
*/
@Service
public class StorageContextService {
/**
* 自动注入策略类
*/
@Autowired
private final Map<String, StorageStrategy> strategyMap = new ConcurrentHashMap<>();
/**
*
* 构造方法</br>
*
* @author syp1293
* @since V1.0.0<br>
* 2022年5月26日 下午2:09:23 </br>
* @param strategyMap 参数map
*/
public StorageContextService(Map<String, StorageStrategy> strategyMap) {
this.strategyMap.clear();
strategyMap.forEach(strategyMap::put);
}
/**
*
* <b>Function:downloadFile</b><br/>
* 下载文件<br/>
* <b>Constraint:</b><br/>
* <br/>
*
* @author syp1293
* @since V1.0.0<br>
* 2022年5月26日 下午2:09:38 </br>
* @param dto 下载参数
*/
public void downloadFile(FileDownloadInputDto dto) {
// 设置默认存储类型
if (StringUtils.isBlank(dto.getStorageType())) {
dto.setStorageType(StorageTypeEnum.MINIO.getValue());
}
strategyMap.get(dto.getStorageType() + "StorageStrategy").downloadFile(dto);
}
}
4.使用
使用的时候,将StorageContextService上下文注入到需要调用的类即可,比如,在controller层注入,或其他需要使用的位置注入。我这里是因为一些原因,所以是在service中注入的,尽量贴全了,代码如下:
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.neuxa.springbreeze.core.lang.BusinessException;
import com.neuxa.springbreeze.document.domain.model.DocumentFile;
import com.neuxa.springbreeze.document.domain.service.FileDomainService;
import com.neuxa.springbreeze.document.dto.FileDownloadInputDto;
import com.neuxa.springbreeze.document.service.FileService;
/**
* 下载文件控制器.</br>
*
* @author syp1293
* @since V1.0.0<br>
* 2022年5月26日 上午11:08:18</br>
*/
@Controller
@RequestMapping("/download")
public class DownloadController {
/**
* fileService注入
*/
@Autowired
private FileService fileService;
/**
* fileDomainService注入
*/
@Autowired
private FileDomainService fileDomainService;
/**
* <b>Function:downloadFile</b><br/>
* 下载文件<br/>
* <b>Constraint:</b><br/>
* <br/>
*
* @author syp1293
* @since V1.0.0<br>
* 2022年5月26日 上午11:08:44 </br>
* @param dto 参数实体
* @param response 响应
*/
@RequestMapping(value = "/download", method = RequestMethod.GET)
@ResponseBody
public void downloadFile(@RequestParam("fileId") String fileId, @RequestParam("storageType") String storageType,
HttpServletResponse response) {
try {
FileDownloadInputDto dto = new FileDownloadInputDto();
dto.setFileId(UUID.fromString(fileId));
dto.setStorageType(storageType);
// 1.根据文件id查询文件名称,后缀等信息,2.设置response
List<UUID> list = new ArrayList<>();
list.add(dto.getFileId());
List<DocumentFile> docFileList = fileDomainService.findByIds(list);
if (docFileList != null && docFileList.size() > 0) {
if (StringUtils.isBlank(storageType)) {
dto.setStorageType(docFileList.get(0).getStorageType());
}
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode(docFileList.get(0).getFileName(), "UTF-8"));
dto.setOutputStream(response.getOutputStream());
fileService.downloadStorageFile(dto);
} else {
throw new BusinessException("未找到文件");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
FileServiceImpl代码:
public class FileServiceImpl extends ApplicationServiceBase implements FileService {
@Autowired
private StorageContextService storageContextService;
public void downloadStorageFile(FileDownloadInputDto dto) {
if (dto != null) {
// 根据storageType走不同策略进行下载
storageContextService.downloadFile(dto);
}
}
}