0、文件下载-MP4分段下载、pdf多图片下载、zip压缩下载、普通下载
package com.northking.core.service.business;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.northking.base.limiter.LimitDownload;
import com.northking.common.cache.CfgMetadataCache;
import com.northking.common.cache.SysConfigCache;
import com.northking.common.constant.Constants;
import com.northking.common.constant.SysConfigConstants;
import com.northking.common.core.redis.RedisCache;
import com.northking.common.domain.entity.business.BusinessFile;
import com.northking.common.domain.entity.business.BusinessFileVerDownload;
import com.northking.common.domain.entity.business.BusinessFileVersion;
import com.northking.common.domain.entity.business.TokenSecretParams;
import com.northking.common.domain.vo.business.BusinessFileQueryVo;
import com.northking.common.enums.IsNoEnum;
import com.northking.common.enums.ResultEnum;
import com.northking.common.mapper.BusinessFileMapper;
import com.northking.common.mapper.BusinessFileVersionMapper;
import com.northking.common.utils.FileTokenUtils;
import com.northking.common.utils.aes.AESUtils;
import com.northking.common.utils.file.ImageUtils;
import com.northking.common.utils.file.ZipFileUtil;
import com.northking.core.Entity.cache.DownloadFileUrlCache;
import com.northking.core.Entity.request.RequestToDownloadFile;
import com.northking.core.Entity.request.RequestToMutiDownloadFile;
import com.northking.core.Entity.response.ResponseBody;
import com.northking.core.service.file.DownloadFileService;
import org.apache.commons.lang3.RandomUtils;
import org.apache.http.protocol.HTTP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.util.UriUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
/**
* <p>
* 文件下载 服务实现类
* </p>
*
* @author northking
* @since 2021-04-28
*/
@Service
public class BusinessDownloadFileServiceImpl extends ServiceImpl<BusinessFileMapper, BusinessFile> {
@Autowired
private BusinessFileMapper businessFileMapper;
@Autowired
private SysConfigCache sysConfigCache;
@Autowired
private CfgMetadataCache cfgMetadataCache;
@Autowired
private RedisCache redisCache;
@Autowired
private DownloadFileService downloadFileService;
@Autowired
private BusinessFileVersionMapper businessFileVersionMapper;
@Autowired
private BusinessMetadataService businessMetadataService;
protected final Logger log = LoggerFactory.getLogger(BusinessDownloadFileServiceImpl.class);
/**
* 获取文件二进制流
* 根据分库分表策略: 加上SysCode和BatchNo分片键对数据库进行操作,否则会增删改查全库!!!
* @param downloadFile 下载文件实体类
* @return
*/
public ResponseBody getFileStream(RequestToDownloadFile downloadFile,HttpServletResponse response, HttpServletRequest request) throws Exception{
log.info("**********单文件下载初始化参数---下载请求参数为:{}",JSONObject.toJSONString(downloadFile));
ResponseBody ret = checkParamsLegal(downloadFile,response,request);
if (ret != null) return ret;
BusinessFileVerDownload file = getFile(downloadFile); // stream: 获取指定文件的下载文件信息
downLoadStreamFile(downloadFile, file, response, request); // 根据文件信息、文件流,向响应里填充文件信息
return null;
}
public void downLoadStreamFile(RequestToDownloadFile downloadFile, BusinessFileVerDownload verDownload, HttpServletResponse response, HttpServletRequest request) throws Exception{
InputStream stream = verDownload.getStream();
BusinessFileVersion fileInfo = verDownload.getFileInfo();
String fileName = fileInfo.getFileName();
String isZipSave = fileInfo.getIsZipSave();
//单文件上传 上传统一流处理 影响待分析 若为压缩存储,进行解压并将字节数组存放为字节数组流
if(IsNoEnum.IS.getValue().equals(isZipSave)){
int downloadFileSize = stream.available();
byte[] buffer = new byte[downloadFileSize];
stream.read(buffer);
//进行解压处理
byte[] bytes = ZipFileUtil.decompressOneByZip(buffer);
stream=new ByteArrayInputStream(bytes);
}
response.reset(); // 清空response
response.addHeader(Constants.RESPONSE_HEADER_CONTENT_DISPOSITION, "attachment;filename=" + UriUtils.encode(fileName, Charset.forName(Constants.UTF8))); // 设置response的Header
response.setContentType(Constants.DOWNLOAD_CONTENT_TYPE_VAL); // 描述消息内容类型( 二进制流,不知道下载文件类型)
response.setHeader(Constants.ACCESS_CONTROL_EXPOSE_HEADERS,Constants.RESPONSE_HEADER_CONTENT_DISPOSITION);
extracted(downloadFile, response, stream, request);
log.info("************************返回一个附件************************");
// 文件流形式下载文件,必须返回null,否则出现convert issue
}
/**
* 获取文件base64
* @param downloadFile 下载文件实体类
* @return
*/
public ResponseBody getFileBase64(RequestToDownloadFile downloadFile,HttpServletResponse response, HttpServletRequest request) throws Exception {
ResponseBody ret = checkParamsLegal(downloadFile,response,request);
if (ret != null) return ret;
BusinessFileVerDownload verDownload = getFile(downloadFile); // base64: 获取指定文件的下载文件信息
InputStream stream = verDownload.getStream();
// 转换格式
String base64Str = Base64.encode(stream);
InputStream base64Stream = new ByteArrayInputStream(base64Str.getBytes(StandardCharsets.UTF_8));
extracted(downloadFile, response, base64Stream, request);
return ResponseBody.success();
}
/**
* 限速下载
* @param downloadFile
* @param response
* @param inputStream
* @throws IOException
* @throws InterruptedException
*/
private void extracted(RequestToDownloadFile downloadFile, HttpServletResponse response, InputStream inputStream, HttpServletRequest request) throws Exception{
String rangeString = request.getHeader(Constants.VIDEO_DOWNLOAD_SIZE_RANGE);
if(rangeString != null) {
downloadRangeFile(inputStream, response, request);
} else {
// 非mp4下载
if (downloadFile != null && downloadFile.isLimit()) {
LimitDownload.limitDownloadFileBySpeed(inputStream, downloadFile.getSpeed(), response);
} else {
downloadFile(inputStream, response);
}
}
}
/**
* 限速下载 - 根据文件存储路径
* @param downloadFile
* @param response
* @param filePath
* @throws IOException
* @throws InterruptedException
*/
private void extracted(RequestToDownloadFile downloadFile, HttpServletResponse response, String filePath, HttpServletRequest request) throws Exception {
FileInputStream fileInputStream = new FileInputStream(filePath);
extracted(downloadFile,response,fileInputStream,request);
}
/**
* 流形式下载文件
* @param fis
* @param response
* @throws IOException
*/
private void downloadFileUnlimited(InputStream fis,HttpServletResponse response, HttpServletRequest request) throws IOException{
String rangeString = request.getHeader(Constants.VIDEO_DOWNLOAD_SIZE_RANGE);
if(rangeString != null) {
downloadRangeFile(fis, response, request);
} else {
downloadFile(fis, response);
}
}
/**
* 文件流下载输出
* @param fis
* @param response
*/
private void downloadFile(InputStream fis,HttpServletResponse response) throws IOException{
OutputStream toClient = null;
try {
int downloadFileSize = fis.available();
byte[] buffer = new byte[downloadFileSize];
fis.read(buffer);
toClient = response.getOutputStream();
toClient.write(buffer);
toClient.flush();
} catch (IOException e) {
log.error("文件下载异常 - 异常信息 \n{}",e);
throw e;
} finally {
if(fis != null ) {
try {
fis.close();
} catch (IOException e) {
log.error("文件下载Finally - 关闭输入流异常 \n{}",e);
}
}
if (toClient != null ) {
try {
toClient.close();
} catch (IOException e) {
log.error("文件下载Finally - 关闭输出流异常 \n{}",e);
}
}
}
}
/**
* 视频流-分片下载
* 根据请求头获取参数下载,并响应返回range
* @param fis 文件流
* @param response
* @param request
* @throws IOException
*/
private void downloadRangeFile(InputStream fis,HttpServletResponse response, HttpServletRequest request) throws IOException{
//获取从那个字节开始读取文件
String rangeString = request.getHeader(Constants.VIDEO_DOWNLOAD_SIZE_RANGE);
OutputStream toClient = null;
try {
int fileLength = fis.available();
if(rangeString != null){
long startIndex = Long.valueOf(rangeString.substring(rangeString.indexOf("=") + 1, rangeString.indexOf("-")));
int fileDownSize = BigDecimal.valueOf(fileLength).subtract(BigDecimal.valueOf(startIndex)).intValue();
int fileDownloadSize = 1024 * 1000; // 1M一个返回响应
//设置内容类型
response.setHeader(Constants.RESPONSE_HEADER_CONTENT_TYPE,Constants.VIDEO_DOWNLOAD_CONTENT_TYPE);
//设置此次相应返回的数据长度
response.setHeader(Constants.RESPONSE_HEADER_CONTENT_LENGTH, String.valueOf(fileDownloadSize));
//设置此次相应返回的数据范围
BigDecimal endIndex = BigDecimal.valueOf(startIndex).add(BigDecimal.valueOf(fileDownloadSize)).subtract(BigDecimal.valueOf(1));
response.setHeader(Constants.RESPONSE_HEADER_CONTENT_RANGE,
"bytes "
+startIndex+"-"
+(endIndex.compareTo(BigDecimal.valueOf(fileDownloadSize)) > 0 ? fileLength-1 : endIndex)
+"/"+fileLength); // Content-Range: bytes 2818048-2861992/2861993
//返回码需要为206,而不是200
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
response.setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_KEEP_ALIVE);
//设定文件读取开始位置(以字节为单位)
fis.skip(startIndex);
// int downloadFileSize = fis.available(); // fis.available(); 1024 * 1000
byte[] buffer = new byte[1024 * 1000]; //
fis.read(buffer);
toClient = response.getOutputStream();
toClient.write(buffer);
toClient.flush();
}
} catch (IOException e) {
log.error("文件Range下载异常 - 异常信息 \n{}",e);
throw e;
} finally {
if(fis != null ) {
try {
fis.close();
} catch (IOException e) {
log.error("文件Range下载Finally - 关闭输入流异常 \n{}",e);
}
}
if(toClient != null ) {
try {
toClient.close();
} catch (IOException e) {
log.error("文件Range下载Finally - 关闭输出流异常 \n{}",e);
}
}
}
}
/**
* 获取下载文件流
* @param downloadFile
* @return
* @throws IOException
*/
public BusinessFileVerDownload getFile(RequestToDownloadFile downloadFile) throws IOException{
String fileId = downloadFile.getFileId();
String batchNo = downloadFile.getBatchNo();
String sysCode = downloadFile.getSysCode();
boolean thumbDownload = downloadFile.isThumbDownload(); // false:默认原图
boolean markDownload = downloadFile.isNoMarkDownload(); // false:默认加水印
BusinessFileVerDownload verDownload = new BusinessFileVerDownload();
//查询文件信息
BusinessFileVersion fileInfo = businessFileVersionMapper.getFileVersionByIdAndSysCodeAndBusiNo(fileId, sysCode, batchNo);
if(Objects.isNull(fileInfo)){
throw new RuntimeException("********查不到指定的文件********");
}
BusinessFile businessFile = new BusinessFile();
BeanUtils.copyProperties(fileInfo,businessFile);
String imgDownloadMethod = thumbDownload?Constants.IMAGE_DOWNLOAD_METHOD_THUMB:""; //单个文件下载-图片原图下载,可用户指定是否缩略图下载
String addWatermarkMethod = markDownload?Constants.IMAGE_DOWNLOAD_METHOD_NO_MARK :""; //单个文件下载-图片原图下载,可用户指定是否加水印下载
businessFile.setImgDownloadMethod(imgDownloadMethod);
businessFile.setAddWatermarkMethod(addWatermarkMethod);
businessFile.setOperId(downloadFile.getOperId());
// fileInfo.getBucketId(),fileInfo.getBucketKey(),saveType,sysCode,imgDownloadMethod,addWatermarkMethod
InputStream stream = downloadFileService.getFileStream(businessFile); //单个文件下载,图片原图下载,按需加水印
verDownload.setStream(stream);
verDownload.setFileInfo(fileInfo);
return verDownload;
}
/**
* 参数合法性校验
* @param downloadFile
* @return
*/
private ResponseBody checkParamsLegal(RequestToDownloadFile downloadFile,HttpServletResponse response, HttpServletRequest request) throws Exception {
String metadataName = downloadFile.getMetadataName();
String ownerSysCode = downloadFile.getOwnerSysCode();
String batchNo = downloadFile.getBatchNo();
String fileId = downloadFile.getFileId();
//1. token验证
TokenSecretParams tokenSecretParams = new TokenSecretParams();
tokenSecretParams.setBatchNo(batchNo);
tokenSecretParams.setFileId(fileId);
tokenSecretParams.setRandom(downloadFile.getRandom());
tokenSecretParams.setTimeOut(downloadFile.getTimeOut()+"");
tokenSecretParams.setIsLimit(downloadFile.isLimit());
tokenSecretParams.setSysCode(downloadFile.getSysCode());
tokenSecretParams.setSpeed(downloadFile.getSpeed()+"");
String tokenStr = FileTokenUtils.getTokenString(tokenSecretParams);
String token = SecureUtil.md5(AESUtils.encryptPwd(tokenStr));
if (!token.equals(downloadFile.getToken())){
//token校验不通过
// return ResponseBody.error(ResultEnum.SYS_ERR.getTransCode(),String.format("url中token校验不通过,token:[%s]", downloadFile.getToken()));
}
//2. 重放攻击 以及 超时校验
DownloadFileUrlCache urlCache = redisCache.getCacheObject(String.format("%s:%s", batchNo, fileId));
int fileUrlPerSecNum = Integer.parseInt(sysConfigCache.getConfigValueByConfigKey(SysConfigConstants.FILE_URL_PER_SEC_ACCESS_NUM));
if (null == urlCache ){
Integer timeout = downloadFile.getTimeOut()!=null?downloadFile.getTimeOut():Integer.parseInt(Constants.timeOut);
//首次访问
redisCache.setCacheObject(String.format("%s:%s", batchNo, fileId),new DownloadFileUrlCache(1,System.currentTimeMillis(), timeout));
}else {
//非首次访问
Integer accessNum = urlCache.getAccessNum();
if (accessNum > fileUrlPerSecNum){
redisCache.deleteObject(String.format("%s:%s", batchNo, fileId));
return ResponseBody.error(ResultEnum.SYS_ERR.getTransCode(),"每秒访问频率过高,请稍后访问");
}
if (urlCache.getFirstAccessTime()+urlCache.getTimeOutSec()+1000 > System.currentTimeMillis()){
redisCache.deleteObject(String.format("%s:%s", batchNo, fileId));
return ResponseBody.error(ResultEnum.SYS_ERR.getTransCode(),"下载文件URL超时,请重试");
}
}
//3. 权限验证,验证访问系统是否拥有访问此资源的权限
//访问系统
//生成流水的系统
//3.跨系统访问其他系统文件验证
/* if(StrUtil.isNotBlank(metadataName) && StrUtil.isNotBlank(ownerSysCode))
{
CfgMetadataPo cfgMetadataPo =new CfgMetadataPo();
cfgMetadataPo.setOwnerSysCode(ownerSysCode);
cfgMetadataPo.setMetadataName(metadataName);
businessMetadataService.selectCfgMetadaOneCodeAndSysCodes(cfgMetadataPo,downloadFile,response,request);
}else{
return ResponseBody.error(ResultEnum.SYS_ERR.getTransCode(),String.format("系统[%s]无权下载此文件",sys));
}*/
return null;
}
/**
* 文件打包下载
* @param mutiDownloadFile 下载参数
* @param response 文件返回流响应
* @return
* @throws Exception
*/
public ResponseBody downloadPackageFiles(RequestToMutiDownloadFile mutiDownloadFile, HttpServletResponse response, HttpServletRequest request) throws Exception {
log.info("************************批量ZIP下载---请求参数:{}", JSONObject.toJSONString(mutiDownloadFile));
BusinessFileQueryVo businessFileVo = new BusinessFileQueryVo();
BeanUtils.copyProperties(mutiDownloadFile,businessFileVo);
RequestToDownloadFile downloadFile = mutiDownloadFile.getDownloadFile();
List<BusinessFile> fileList = businessFileMapper.businessFileList(businessFileVo);
if(CollectionUtils.isEmpty(fileList)){
throw new RuntimeException("无法找到您的文件,请选择正确文件后再试");
}
List fileNames = fileList.stream().map(file->file.getFileName()).distinct().collect(Collectors.toList());
if(fileNames.size() != fileList.size()){
throw new RuntimeException("您选择下载的文件名相同,无法打包下载,请重命名后再试!");
}
//zip批量下载
Map<String,byte[]> srcFilesMap = new HashMap<>();
for (BusinessFile file : fileList) {
file.setOperId(mutiDownloadFile.getOperId());
setFileList(file, srcFilesMap);
}
try {
byte[] bytes = ZipFileUtil.compressByZip(srcFilesMap); //获取文件流后压缩
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
String fileName = new Date().getTime() + "_"+ RandomUtils.nextLong()+".zip";
response.reset(); // 清空response
response.addHeader(Constants.RESPONSE_HEADER_CONTENT_DISPOSITION, String.format("attachment;filename=%s", UriUtils.encode(fileName, Charset.forName(Constants.UTF8)))); // 设置response的Header
response.setContentType(Constants.DOWNLOAD_CONTENT_TYPE_VAL); // 描述消息内容类型( 二进制流,不知道下载文件类型)
response.setHeader(Constants.ACCESS_CONTROL_EXPOSE_HEADERS,Constants.RESPONSE_HEADER_CONTENT_DISPOSITION);
// 下载压缩文件
extracted(downloadFile,response,inputStream,request);
log.info("**************批量下载完成,返回附件**************");
} catch (Exception e) {
throw new RuntimeException("打包过程出错", e);
}
// 文件流形式下载文件,必须返回null,否则出现convert issue
return null;
}
/**
* 多图片文件PDF下载
* @param mutiDownloadFile
* @param response
* @return
* @throws Exception
*/
public ResponseBody downloadPdfForPictures(RequestToMutiDownloadFile mutiDownloadFile, HttpServletResponse response, HttpServletRequest request) throws Exception {
log.info("************************批量PDF下载---请求参数:{}", JSONObject.toJSONString(mutiDownloadFile));
BusinessFileQueryVo businessFileVo = new BusinessFileQueryVo();
BeanUtils.copyProperties(mutiDownloadFile,businessFileVo);
RequestToDownloadFile downloadFile = mutiDownloadFile.getDownloadFile();
List<BusinessFile> fileList= businessFileMapper.businessFileList(businessFileVo);
if(fileList.isEmpty()){
throw new RuntimeException("无法找到您的文件,请选择正确文件后再试!");
}
Date currentDate = new Date();
String fileFullPath = currentDate.getTime() + "_"+ RandomUtils.nextLong() + ".pdf";
log.info("*************************获取PDF路径******************************"+fileFullPath);
OutputStream pdfOutputStream = new FileOutputStream(fileFullPath);
Map<BusinessFile,InputStream> imagesMap = new HashMap<>();
//PDF批量下载 修改为流式穿参
for(BusinessFile file : fileList){
if(ImageUtils.isImage(file.getFileType())) {
// bucketId, bucketKey, storeType,sysCode
file.setOperId(mutiDownloadFile.getOperId());
InputStream stream = downloadFileService.getFileStream(file); //Pdf下载,图片下载为原图,不加水印
imagesMap.put(file,stream);
}
}
ImageUtils.writeImagesToPdf(imagesMap, pdfOutputStream);
response.reset(); // 清空response
response.addHeader(Constants.RESPONSE_HEADER_CONTENT_DISPOSITION, String.format("attachment;filename=%s", UriUtils.encode(fileFullPath, Charset.forName(Constants.UTF8)))); // 设置response的Header
response.setContentType(Constants.DOWNLOAD_CONTENT_TYPE_VAL); // 描述消息内容类型( 二进制流,不知道下载文件类型)
response.setHeader(Constants.ACCESS_CONTROL_EXPOSE_HEADERS,Constants.RESPONSE_HEADER_CONTENT_DISPOSITION);
// 下载pdf文件
extracted(downloadFile, response, fileFullPath,request);
File delPdf = new File(fileFullPath);
log.info("*************************返回PDF附件******************************");
delPdf.delete();// 生成pdf时,写到了硬盘上作为临时文件,在写到response后,需要删除,否则占用空间
return null;
}
/**
* 根据不同存储方式获取文件,并将文件放到文件列表里
* 1、多图片pdf合成下载
* 2、多文件zip打包下载
* @param file
*
* @param filesMap
* @throws IOException
*/
private void setFileList(BusinessFile file,Map<String,byte[]> filesMap) throws Exception{
// bucketId, bucketKey, storeType,sysCode
InputStream stream = downloadFileService.getFileStream(file); //Zip下载,图片下载原图,不加水印
byte[] bytes = new byte[stream.available()];
stream.read(bytes);
//将流进行关闭
try {
stream.close();
}catch (Exception e){
e.printStackTrace();
throw e;
}finally {
if(stream!=null){
stream.close();
}
}
//若为压缩存储,进行解压处理
if(IsNoEnum.IS.getValue().equals(file.getIsZipSave())){
bytes = ZipFileUtil.decompressOneByZip(bytes);
}
filesMap.put(file.getFileName(),bytes);
}
}
1、base64上传
package com.northking.core.service.business;
import cn.hutool.core.thread.NamedThreadFactory;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.northking.common.cache.CfgAccessSystemCache;
import com.northking.common.cache.SysConfigCache;
import com.northking.common.constant.Constants;
import com.northking.common.constant.SysConfigConstants;
import com.northking.common.constant.TableColumnConstants;
import com.northking.common.domain.dto.other.RedisStatisticParams;
import com.northking.common.domain.entity.business.*;
import com.northking.common.domain.entity.business.file.BusinessMetadataDto;
import com.northking.common.domain.entity.business.file.BusinessMetadataInfoRq;
import com.northking.common.domain.entity.params.CfgAccessSystem;
import com.northking.common.elasticsearch.entity.EsBusinessFileEntity;
import com.northking.common.elasticsearch.service.EsFileService;
import com.northking.common.mapper.BusinessBatchMapper;
import com.northking.common.mapper.BusinessFileMapper;
import com.northking.common.mapper.BusinessFileVersionMapper;
import com.northking.common.utils.FileTokenUtils;
import com.northking.common.utils.FileUtil;
import com.northking.common.utils.StringUtils;
import com.northking.common.utils.file.ImageUtils;
import com.northking.common.utils.statistic.StatisticUtils;
import com.northking.core.Entity.base.BaseBusinessBo;
import com.northking.core.Entity.base.BaseRequestBody;
import com.northking.core.Entity.bo.BatchUploadFileBo;
import com.northking.core.Entity.bo.UploadsFileBo;
import com.northking.core.Entity.request.RequestBodyToBatchUploadFile;
import com.northking.core.Entity.response.ResponseBody;
import com.northking.core.constants.RequestFieldConstants;
import com.northking.core.service.base.AbstractBusiService;
import com.northking.core.service.file.CommonFileService;
import com.northking.core.service.file.UploadFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import static java.lang.String.format;
@Slf4j
@Service
public class ServiceOfUploadBatchUploadFile extends AbstractBusiService {
@Autowired
public CfgAccessSystemCache accessSystemCache;
@Autowired
public SysConfigCache sysConfigCache;
@Resource
private BusinessBatchMapper businessBatchMapper;
@Resource
private BusinessFileMapper businessFileMapper;
@Resource
private BusinessFileVersionMapper businessFileVersionMapper;
@Autowired
private UploadFileService uploadFileService;
@Autowired
private CommonFileService commonFileService;
@Autowired
private BusinessMetadataService businessMetadataService;
@Autowired
private BusinessFileInfoService businessFileInfoService;
@Autowired
private EsFileService esFileService;
private ExecutorService saveExecutorService = Executors.newCachedThreadPool(new NamedThreadFactory("SaveFileThreads-",false));
private ExecutorService uploadExecutorService = Executors.newCachedThreadPool(new NamedThreadFactory("UploadFileThreads-",false));
/**
* 请求报文校验
*
* @param jsonObject
* @return
*/
@Override
public BaseRequestBody checkParam(JSONObject jsonObject) {
String bizNo = jsonObject.getString(RequestFieldConstants.BUSINO);
String sysId = jsonObject.getString(RequestFieldConstants.SYS_ID);
String token = jsonObject.getString(Constants.TOKEN);
BaseRequestBody builder = new RequestBodyToBatchUploadFile.Builder()
.setBusiNo(bizNo)
.setToken(token) // 返回上传后图片完全下载路径
.checkIsValidAccessSys(bizNo, sysId)
.setFileList(jsonObject.getJSONArray(RequestFieldConstants.FILE_LIST).toJavaList(RequestBodyToBatchUploadFile.FileInfo.class))
.setSysId(sysId)
.setTradeCode(jsonObject.getString(RequestFieldConstants.TRADE_CODE))
.setSysKey(jsonObject.getString(RequestFieldConstants.SYS_KEY))
.setOrderId(jsonObject.getIntValue(RequestFieldConstants.ORDET_ID))
.setOperId(jsonObject.getString(RequestFieldConstants.OPER_ID))
.build();
return builder;
}
/**
* 保存报文的信息
*
* @param requestBody
* @return
*/
@Override
public boolean saveLog(BaseRequestBody requestBody) {
return true;
}
/***
* 组织响应报文
* @param endBusinessBo
* @return
*/
@Override
public ResponseBody packResult(BaseBusinessBo endBusinessBo) {
BatchUploadFileBo uploadFileBo = (BatchUploadFileBo) endBusinessBo;
List<UploadsFileBo> data = new ArrayList<>();
log.info("*************** 上传文件-整理返回结果开始 ***************");
String fileOnlinePreviewAddr = sysConfigCache.getConfigValueByConfigKey(SysConfigConstants.FILE_ONLINE_PREVIEW_ADDR);
for (BatchUploadFileBo.FileListResult result : uploadFileBo.getRetFileList()) {
UploadsFileBo bo = new UploadsFileBo();
TokenSecretParams tokenSecretParams = new TokenSecretParams();
String token = uploadFileBo.getToken();
String sysCode = result.getSysId();
CfgAccessSystem accessSystem = accessSystemCache.getAccessSystemBySysId(sysCode);
String originalFilePath = getDownloadPath(result, token); // 上传文件,返回文件下载地址
// 1.返回上传后【源文件】的完全下载地址
result.setFileUrl(originalFilePath);
// 2.返回上传后【图片缩略图】的完全下载地址
tokenSecretParams.setThumbDownload(true);
if(ImageUtils.isImageThumbRel(result.getFileType(), sysCode)){
result.setImageThumbPath(getDownloadPath(result, token,"true")); // 上传文件,返回缩略图下载地址
}
//3.返回上传后图片【预览】的完全下载地址
BusinessOnlinePreviewParams previewParams = new BusinessOnlinePreviewParams();
previewParams.setSysCode(sysCode);
previewParams.setFileName(result.getFileName());
previewParams.setFilePath(originalFilePath);
previewParams.setCurrentSystemName(accessSystem.getSysName());
previewParams.setCurrentUser(result.getOperaId());
previewParams.setPreviewFileNo(result.getFileId()+sysCode+result.getBusiNo()); // 预览-文件缓存名称
bo.setFileOnlinePreviewAddr(fileOnlinePreviewAddr + FileTokenUtils.getPreviewSecretParams(previewParams)); //base上传文件-返回预览地址
BeanUtils.copyProperties(result, bo);
data.add(bo);
}
ResponseBody body = ResponseBody.success();
body.put(ResponseBody.TRADE_DATA, data);
log.info("*************** 上传文件-整理返回结果结束。返回结果为:{} ***************",JSONObject.toJSONString(body));
return body;
}
/**
* 扩展任务逻辑(图片转缩略图,ocr,pdf转换)
*
* @param baseBusinessBo
* @return
*/
@Override
public BaseBusinessBo endCoreWork(BaseBusinessBo baseBusinessBo) {
return baseBusinessBo;
}
/***
* 执行核心操作
* @param requestBody
* @return
*/
@Override
public BaseBusinessBo doCoreWork(BaseRequestBody requestBody) {
log.info("*************** Base64批量上传文件开始...***************");
BatchUploadFileBo uploadFileBo = new BatchUploadFileBo();
Date currentDate = new Date();
RequestBodyToBatchUploadFile body = (RequestBodyToBatchUploadFile) requestBody;
String bizNo = body.getBusiNo();
String sysId = body.getSysId();
String operId = body.getOperId();
// 无流水号,则新增流水信息
BusinessBatch batch = businessBatchMapper.selectSysIdAndSysCode(bizNo, sysId);
if (batch == null || StringUtils.isEmpty(batch.getBatchNo())) {
BusinessBatch businessBatch = new BusinessBatch();
businessBatch.setBatchNo(bizNo);
businessBatch.setSysCode(sysId);
businessBatch.setCreatedBy(operId);
businessBatch.setCreatedTime(currentDate);
businessBatch.setUpdatedTime(currentDate);
businessBatch.setStatus(Constants.VALID);
int insertResult = businessBatchMapper.insert(businessBatch);
log.info("流水号加缓存 - 流水统计-小文件上传-无流水号时创建流水-开始");
RedisStatisticParams statisticParams = new RedisStatisticParams();
statisticParams.setOperName("流水统计-小文件上传-无流水号时创建流水");
statisticParams.setOperResult(insertResult);
statisticParams.setSysCode(businessBatch.getSysCode());
StatisticUtils.increaseForBatch(statisticParams);
log.info("流水号加缓存 - 流水统计-小文件上传-无流水号时创建流水-开始");
log.info("************** 向数据库新增不存在batchId的业务流信息 **************");
}
//标记文件上传顺序
// AtomicInteger count = new AtomicInteger(1);
//获取异步返回结果
List<Future<BatchUploadFileBo.FileListResult>> futures = new ArrayList<>();
for (RequestBodyToBatchUploadFile.FileInfo info : body.getFileList()) {
String fileId = UUID.randomUUID().toString().replace("-","");
info.setOperaId(operId);
info.setFileId(fileId);
info.setSysCode(body.getSysId());
info.setBatchNo(body.getBusiNo());
info.setToken(body.getToken());
//使用自定义线程池,保证任务互不影响
Future<BatchUploadFileBo.FileListResult> future = saveExecutorService.submit(() -> getResult(info,body));
futures.add(future);
}
//循环获取结果
List<BatchUploadFileBo.FileListResult> resultList = futures.stream().map(future -> {
BatchUploadFileBo.FileListResult fileResult = new BatchUploadFileBo.FileListResult();
try {
fileResult = future.get();
} catch (InterruptedException | ExecutionException e) {
log.error("影像批量上传失败,线程中断:{}", e);
}
return fileResult;
}).collect(Collectors.toList());
uploadFileBo.setRetFileList(resultList);
uploadFileBo.setToken(requestBody.getToken()); // 返回上传后图片完全下载路径
log.info("*************** Base64批量上传文件结束 ***************");
return uploadFileBo;
}
private BatchUploadFileBo.FileListResult getResult(RequestBodyToBatchUploadFile.FileInfo info,RequestBodyToBatchUploadFile body) {
log.info("*************** Base64上传文件开始... ***************");
BatchUploadFileBo.FileListResult result = new BatchUploadFileBo.FileListResult();
String fileBase64 = info.getBase64();
String fileType = info.getFileType();
String targetFileId = info.getTargetFileId();
String fileId = info.getFileId();
try {
// AA.上传实际文件 ---- 不返回上传结果,用 execute,性能好(可查看历史记录);返回上传结果,用submit,返回结果
Future<Boolean> future = uploadExecutorService.submit(()->{
String sysCode = info.getSysCode();
String batchNo = info.getBatchNo();
log.info("上传文件开始");
boolean uploadRet = uploadFileService.uploadFile2Base64(sysCode, batchNo,
fileId, fileBase64, fileType);
log.info("上传文件结束");
if (!uploadRet) {
log.error("真实上传文件失败:{},{},{}", sysCode,batchNo,fileId);// throw new RuntimeException
}
return uploadRet;
});
boolean uploadResult = future.get();
result.setUploadTrueFileSucc(uploadResult); // 实际文件上传结果返回
result.setFileId(fileId);
result.setTargetFileId(targetFileId); // 更新时使用
// 水印 + ocr识别使用
result.setFileType(fileType);
//保存数据库
if(uploadResult) {
int dbUploadResult = saveBusinessToDataBase(info, result, body);
// AA.上传文件信息 ---- 历史文件表插入成功算上传成功
result.setUploadFileInfoSucc(dbUploadResult >0 ? true: false);
}
log.info("*************** Base64上传文件结束 ***************");
} catch (Exception e) {
result.setSuccess(false);
result.setBillType(info.getBillType());
log.error("-------------上传异常请求: {}", JSONObject.toJSONString(result));
}
return result;
}
/**
* 保存文件表,文件版本表,文件源数据表
* // !!!!!! 修改此方法,需要同步 BusinessFileInfoServiceImpl.saveBusinessToDataBase(...) !!!!!!
*
* @param info 各个文件的请求参数
* @param result 各个文件数据库存储结果
*/
@Transactional
public int saveBusinessToDataBase(RequestBodyToBatchUploadFile.FileInfo info, BatchUploadFileBo.FileListResult result,RequestBodyToBatchUploadFile body) {
String dbOperMsg = "";
Date currentDate = new Date();
String targetFileId = result.getTargetFileId();
log.info("************** 向数据库保存文件存储成功信息开始 ... **************");
String fileId = result.getFileId();
String bizNo = info.getBatchNo();
String sysId = info.getSysCode();
String fileName = info.getFileName();
String fileVer = commonFileService.getFileVersion(fileId);
// 获取文件下载全路径地址 - 用于ocr识别
result.setBusiNo(bizNo);
result.setSysId(sysId);
String fileType = info.getFileType();
String operaId = info.getOperaId();
String bucket = commonFileService.getBucket(sysId, bizNo); // file storage full folder path
String key = commonFileService.getKey(sysId, bizNo, fileId, fileType); // full file name
String filePath = format("%s%s", bucket, key);
log.info("************** 向数据库保存文件存储请求主参数:fileId: {};bizNo:{}; sysId:{} **************", fileId, bizNo, sysId);
String userBillType = StringUtils.isNotEmpty(info.getBillType()) ? info.getBillType() : Constants.NO_CLASSIFY;
// 返回用
result.setBusiNo(bizNo);
result.setSysId(sysId);
result.setFileName(fileName);
result.setBillType(userBillType);
result.setOperaId(operaId);
BusinessFile businessFile = new BusinessFile();
//数据库存储文件时,保存zip标识。图片上传即压缩
String isZipSave = Constants.ONE_STR;
boolean isDealImage = ImageUtils.isImageThumbRel(fileType,sysId);
boolean isMp4 = "mp4".equals(fileType);
if(!isDealImage && !isMp4){
CfgAccessSystem accessSystem = accessSystemCache.getAccessSystemBySysId(sysId);
isZipSave = accessSystem.getIsZipSave();
}
businessFile.setIsZipSave(isZipSave);
businessFile.setFileId(fileId);
businessFile.setFileSource("2001");
businessFile.setBillCode(userBillType);
businessFile.setFileName(fileName);
// nas存储需要加密,则再获取md5
if (commonFileService.getFileSaveIsEncryption()) {
businessFile.setMd5(FileUtil.getFileMd5(info.getBase64()));
}
businessFile.setCreatedBy(operaId);
businessFile.setCreatedTime(currentDate); // 新增时默认更新时间有值
businessFile.setStatus(Constants.VALID);
//默认设置为最新文件
businessFile.setVersion(fileVer);
businessFile.setBucketId(bucket);
businessFile.setBucketKey(key);
businessFile.setSaveType(commonFileService.getStorageType());
businessFile.setFileType(fileType);
businessFile.setBatchNo(bizNo);
businessFile.setRemark(info.getRemark());
businessFile.setSysCode(sysId);
businessFile.setFileSize((long) FileUtil.getFileSize(info.getBase64()));
businessFile.setFilePath(filePath);
businessFile.setOrderId(info.getOrderId());
// 替换现有文件
if (StringUtils.isNotEmpty(targetFileId)) {
log.info("************** 已有文件存在,根据已有文件ID更新数据库 - 开始 **************");
// 更新 business_file
businessFile.setFileId(targetFileId);
businessFile.setUpdatedBy(operaId);
businessFile.setUpdatedTime(currentDate);
QueryWrapper<BusinessFile> wrapper = new QueryWrapper<>();
wrapper.eq(TableColumnConstants.SYS_CODE, sysId)
.eq(TableColumnConstants.BATCH_NO, bizNo)
.eq(TableColumnConstants.FILE_ID, targetFileId);
int updResult = businessFileMapper.update(businessFile, wrapper);
if (updResult != 1) {
dbOperMsg = "在已有文件上上传新版本失败,请重试!";
}
log.info("************** 已有文件存在,根据已有文件ID更新数据库{}条数据,错误结果:{} - 结束 **************", updResult, dbOperMsg);
} else {
log.info("************** 向根据业务流id向数据库新增文件信息 - 开始:fileId: {};bizNo:{}; sysId:{} **************", fileId, bizNo, sysId);
businessFile.setFileId(fileId);
int insertResult = businessFileMapper.insert(businessFile);
log.info("文件加缓存 - 文件统计-小文件上传-增加相关统计访问数-开始");
RedisStatisticParams statisticParams = new RedisStatisticParams();
statisticParams.setOperName("文件统计-小文件上传-增加相关统计访问数");
statisticParams.setOperResult(insertResult);
statisticParams.setSysCode(businessFile.getSysCode());
statisticParams.setFileSize(businessFile.getFileSize());
statisticParams.setFileGroupType(FileUtil.getFileGroupType(businessFile.getFileType()));
StatisticUtils.increaseForFile(statisticParams);
log.info("文件加缓存 - 文件统计-小文件上传-增加相关统计访问数-结束");
log.info("************** 根据业务流id向数据库新增文件信息 - 结束 **************");
}
BusinessFileVersion fileVersion = new BusinessFileVersion();
BeanUtils.copyProperties(businessFile, fileVersion);
fileVersion.setVersion(fileVer);
fileVersion.setVerFileId(fileId);
fileVersion.setCreateTime(currentDate);
int insertVerResult = businessFileVersionMapper.insert(fileVersion);
log.info("************** 根据业务流id及文件Id向数据库新增文件版本信息 **************");
// 文件添加共享标签,使用已有标签
String addWarningMsg = addMetadataForFile(info, result);
result.setWarningMsg(addWarningMsg);
// 必须在历史文件新增完,再去调用ocr识别,否则文件无法下载
updateFileInfo(businessFile, fileVersion);
log.info("************** 向数据库保存文件存储成功信息结束 ... **************");
// log.info("---------------------- 保存第{}个文件成功 ----------------------", count);
// count.getAndIncrement(); // 存到磁盘且数据库存储成功后,再加一
// TODO 机构号 柜员号
EsBusinessFileEntity esBusinessFileEntity = new EsBusinessFileEntity(StringUtils.isNotEmpty(targetFileId)?targetFileId:fileId,
sysId, null, businessFile.getBatchNo(), fileName, fileType, userBillType,
businessFile.getCreatedBy(), currentDate, null, null,null);
esFileService.addOrUpdEsFile(esBusinessFileEntity);
log.info("************** es保存文件信息 ... **************");
return insertVerResult;
}
/**
* 更新文件 - 文件OCR识别分类
*
* @param businessFile
*/
private void updateFileInfo(BusinessFile businessFile, BusinessFileVersion businessFileVersion) {
businessFileInfoService.updateFileOcrType(businessFile, businessFileVersion);
}
/**
* 获取下载完全路径
* @param result
* @param token
* @param extDownloadRd 扩展请求字段
* 1. extDownloadRd[0] ---> 是否缩略图下载
* @return
*/
private String getDownloadPath(BatchUploadFileBo.FileListResult result, String token, String... extDownloadRd) {
String ipWithPort = sysConfigCache.getConfigValueByConfigKey(SysConfigConstants.SYS_IP_PORT_NEW);
TokenSecretParams tokenSecretParams = new TokenSecretParams();
boolean thumbDownload = false;
if(StringUtils.isNotEmpty(extDownloadRd) && "true".equalsIgnoreCase(extDownloadRd[0])){
thumbDownload = true;
}
tokenSecretParams.setBatchNo(result.getBusiNo());
tokenSecretParams.setFileId(result.getFileId());
tokenSecretParams.setSysCode(result.getSysId());
tokenSecretParams.setToken(token); // 返回上传后图片完全下载路径
tokenSecretParams.setThumbDownload(thumbDownload);
tokenSecretParams.setOperId(result.getOperaId()); // 下载当前操作人
String filePath = Constants.HTTP + ipWithPort + FileTokenUtils.getDownloadPath(tokenSecretParams); // base64上传
return filePath;
}
/**
* 给上传文件添加标签
* 没有标签输入内容,则代表不添加共享标签
* @param info
* @param result
*
* @return
*/
private String addMetadataForFile(RequestBodyToBatchUploadFile.FileInfo info, BatchUploadFileBo.FileListResult result) {
List<RequestBodyToBatchUploadFile.MetadataInfo> metadatas =info.getMetadatas();
// List<BusinessMetadataDto> metadataDtos = Arrays.stream(metadatas.stream().toArray(BusinessMetadataDto[]::new)).collect(Collectors.toList());
List<BusinessMetadataDto> metadataDtos = new ArrayList<>();
if(CollectionUtils.isNotEmpty(metadatas)){
for (RequestBodyToBatchUploadFile.MetadataInfo metadata : metadatas) {
BusinessMetadataDto businessMetadataDto =new BusinessMetadataDto();
businessMetadataDto.setShareToSysCodes(metadata.getShareToSysCodes());
businessMetadataDto.setMetadataName(metadata.getMetadataName());
metadataDtos.add(businessMetadataDto);
}
}
// 为一个文件添加多个标签 - 已有标签,未有标签,标签关联文件
BusinessMetadataInfoRq metadata = new BusinessMetadataInfoRq();
metadata.setSysCode(info.getSysCode());
metadata.setBatchNo(info.getBatchNo());
metadata.setFileId(result.getFileId());
metadata.setCreateBy(info.getOperaId());
metadata.setMetadatas(metadataDtos);
String warningMsg = businessMetadataService.addMetadataForFile(metadata);
return warningMsg;
}
}
2、切片上传-验证文件是否存在、切片、合并文件 & 二进制上传-批量携带文件附属信息
package com.northking.core.service.business.impl;
import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.northking.base.storage.base.IBaseStorage;
import com.northking.base.storage.factory.StorageFactory;
import com.northking.common.cache.CfgAccessSystemCache;
import com.northking.common.cache.SysConfigCache;
import com.northking.common.constant.Constants;
import com.northking.common.constant.SysConfigConstants;
import com.northking.common.constant.TableColumnConstants;
import com.northking.common.domain.dto.other.RedisStatisticParams;
import com.northking.common.domain.entity.business.*;
import com.northking.common.elasticsearch.entity.EsBusinessFileEntity;
import com.northking.common.domain.entity.business.file.*;
import com.northking.common.domain.entity.params.CfgAccessSystem;
import com.northking.common.domain.vo.extend.IdentifiedPicInfoRq;
import com.northking.common.domain.vo.extend.IdentifiedPicRq;
import com.northking.common.elasticsearch.service.EsFileService;
import com.northking.common.enums.IsNoEnum;
import com.northking.common.mapper.*;
import com.northking.common.utils.FileTokenUtils;
import com.northking.common.utils.FileUtil;
import com.northking.common.utils.StringUtils;
import com.northking.common.utils.file.ImageUtils;
import com.northking.common.utils.file.ZipFileUtil;
import com.northking.common.utils.statistic.StatisticUtils;
import com.northking.common.utils.uuid.UUID;
import com.northking.core.config.ExtendsConfiguration;
import com.northking.core.service.business.BusinessFileInfoService;
import com.northking.core.service.business.BusinessMetadataService;
import com.northking.core.service.business.BusinessSystemService;
import com.northking.core.service.file.CommonFileService;
import com.northking.core.service.file.UploadFileService;
import com.northking.core.utils.FileInfoUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static java.lang.String.format;
@Service
@Slf4j
public class BusinessFileInfoServiceImpl implements BusinessFileInfoService {
@Autowired
public CfgAccessSystemCache accessSystemCache;
@Autowired
public SysConfigCache sysConfigCache;
@Autowired
private CommonFileService commonFileService;
@Autowired
private UploadFileService uploadFileService;
@Autowired
private BusinessSystemService businessSystemService;
@Autowired
private BusinessTrunckInfoMapper chunkInfoMapper;
@Autowired
private BusinessBatchMapper businessBatchMapper;
@Autowired
private BusinessMetadataService businessMetadataService;
@Autowired
private BusinessFileMapper businessFileMapper;
@Autowired
private BusinessFileVersionMapper businessFileVersionMapper;
@Autowired
private EsFileService esFileService;
private final static Logger logger = LoggerFactory.getLogger(BusinessFileInfoServiceImpl.class);
private ExecutorService fileSaveExeService = Executors.newCachedThreadPool(new NamedThreadFactory("FileSaveExecuteService-",false));
@Override
public int mergeFile(BusinessTrunckFileInfo fileInfo) throws Exception{
logger.info("***********************获取切片合并文件参数{}:"+ JSONObject.toJSONString(fileInfo));
int statusCode = HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
// 获取必要参数
String fileMd5 = fileInfo.getIdentifier();
String fileName = fileInfo.getFileName();
String bizNo = fileInfo.getBatchNo();
String sysId = fileInfo.getSysCode();
String fileType = StringUtils.isNotEmpty(fileName) && fileName.lastIndexOf(".")>0?fileName.substring(fileName.lastIndexOf(".")+1):"";
String folder = commonFileService.getChunkBucket(fileInfo.getSysCode(),fileInfo.getBatchNo(),fileMd5);
String file = folder + commonFileService.getChunkFullKey(fileMd5,fileType);
String fileNewName = commonFileService.getChunkFullKey(fileMd5,fileType);
logger.info("***********************获取文件名称******************************"+fileNewName);
boolean uploadSucc = false;
String fileId = RandomUtil.randomString(32);
String bucket = commonFileService.getBucket(sysId, bizNo); // file storage full folder path
String key = commonFileService.getKey(sysId, bizNo, fileId, fileType); // full file name
boolean isEncryption = commonFileService.getFileSaveIsEncryption();
String filePath = format("%s%s", bucket, key);
logger.info("***********************获取文件路径******************************"+filePath);
// 填充必要参数:文件存储+数据库记录
fileInfo.setBucket(bucket);
fileInfo.setKey(key);
fileInfo.setFileId(fileId);
fileInfo.setFileExtType(fileType);
fileInfo.setFileFullPath(filePath); // 不同类型存储方式,filePath不同
// 查找是否有相同存储文件
BusinessFile businessFile = new BusinessFile();
businessFile.setMd5(fileMd5);
businessFile.setSysCode(sysId);
businessFile.setBatchNo(bizNo);
businessFile.setSaveType(commonFileService.getStorageType());
BusinessFile businessFileRs = businessSystemService.doesFileTrulyExist(businessFile);
/* 是否有相同存储文件:有相同文件 - 开始*/
if(StringUtils.isNotEmpty(businessFileRs.getMd5())){
// 获取相同文件信息后,向数据库写入文件相关的参数数据
logger.info("************** 有相同存储文件:获取相同文件信息,保存文件信息到库 开始 ... **************");
// 复制相同文件的路径到新文件里
fileInfo.setBucket(businessFileRs.getBucketId());
fileInfo.setKey(businessFileRs.getBucketKey());
fileInfo.setFileFullPath(businessFileRs.getFilePath());
saveBusinessToDataBase(fileInfo,null);
statusCode = HttpServletResponse.SC_OK;
logger.info("************** 有相同存储文件:获取相同文件信息,保存文件信息到库 结束 **************");
return statusCode;
}
/* 是否有相同存储文件:有相同文件 - 结束*/
/* 是否有相同存储文件:无相同文件 - 开始*/
logger.info("************** 没有相同存储文件:存储文件,保存文件信息到库 开始 ... **************");
Integer fileSuccess = FileInfoUtils.merge(file, folder, fileNewName.substring(1)); // 文件名去掉路径斜线
// 合并成功后,删除切片块文件
fileInfo.setLocation(folder);
QueryWrapper<BusinessTrunckInfo> wrapper = new QueryWrapper<>();
wrapper.eq(BusinessTrunckInfo.COL_SYS_CODE, sysId)
.eq(BusinessTrunckInfo.COL_BATCH_NO, bizNo)
.eq(BusinessTrunckInfo.COL_IDENTIFIER, fileMd5)
.eq(BusinessTrunckInfo.COL_FILE_NAME, fileName);
chunkInfoMapper.delete(wrapper);
String isZipSave = dealWithOriginalFile(fileInfo, businessFile, isEncryption, fileName, file);
// 文件合并成功后:nas-1)移动文件到目标路径; ceph-1)调用ceph分片上传存储; 2)保存记录到库
if (fileSuccess == HttpServletResponse.SC_OK) {
String currentStorageType = commonFileService.getStorageType().toLowerCase(); // nas, ceph ... commonFileService.getStorageType().toLowerCase()
switch (currentStorageType){
case Constants.STORAGE_TYPE_NAS:
// 在原有的nas文件路径下截取文件夹,与后面保存到数据库同步
String targetFileDirectory = filePath.lastIndexOf("/")>0?filePath.substring(0,filePath.lastIndexOf("/")):"";
// 有扩展名的不能丢,无扩展名的不能加“.”
String targetFileName = fileId + (StringUtils.isNotEmpty(fileType)?("." + fileType):"");
// 1)将合成的临时文件进行移动
logger.info("************** 文件合并成功,nas移动文件 开始 ... **************");
uploadSucc = FileInfoUtils.move(file, targetFileDirectory, targetFileName);
logger.info("************** 文件合并成功,nas移动文件 结束 ... **************");
break;
case Constants.STORAGE_TYPE_CEPH:
fileInfo.setNasTempFilePath(file); // 临时源文件,即将要传到ceph上的文件
uploadSucc = uploadFileToCeph(fileInfo);
logger.error("文件上传ceph成功!");
break;
}
if(uploadSucc){
// 2)存储成功后,向数据库写入文件相关的参数数据
logger.info("************** 文件合并、存储成功,保存文件信息到库 结束 ... **************");
//合并文件 增加对数据库文件是否zip的字段保存
saveBusinessToDataBase(fileInfo,isZipSave);
statusCode = HttpServletResponse.SC_OK;
}
}
logger.info("************** 没有相同存储文件:存储文件,保存文件信息到库 结束 **************");
return statusCode;
}
/**
* 保存文件表,文件版本表,文件源数据表
* // !!!!!! 修改此方法,需要同步 ServiceOfUploadBatchUploadFile.saveBusinessToDataBase(...) !!!!!!
* @param fileInfo
*/
private String saveBusinessToDataBase(BusinessTrunckFileInfo fileInfo,String isZipSave) {
String dbOperMsg = "";
Date currentDate = new Date();
String targetFileId = fileInfo.getTargetFileId();
logger.info("************** 向数据库保存文件存储成功信息开始 ... **************");
String fileId = fileInfo.getFileId();
String bizNo = fileInfo.getBatchNo();
String sysId = fileInfo.getSysCode();
String fileName = fileInfo.getFileName();
String billType = StringUtils.isNotEmpty(fileInfo.getBillType())?fileInfo.getBillType():Constants.NO_CLASSIFY;
String operaId = fileInfo.getOperId();
String fileVer = commonFileService.getFileVersion(fileId);
BusinessFile businessFile = new BusinessFile();
businessFile.setIsZipSave(isZipSave);
businessFile.setFileSource("2001");
businessFile.setBillCode(billType);
businessFile.setFileName(fileName);
// 计算或者前端获取
businessFile.setMd5(fileInfo.getIdentifier()); // 前端:identifier();后端:FileUtil.getFileMd5(info.getBase64())
businessFile.setCreatedBy(operaId);
businessFile.setCreatedTime(currentDate);
businessFile.setStatus(Constants.VALID);
//默认设置为最新文件
businessFile.setVersion(fileVer);
businessFile.setBucketId(fileInfo.getBucket());
businessFile.setBucketKey(fileInfo.getKey());
businessFile.setSaveType(commonFileService.getStorageType());
businessFile.setFileType(fileInfo.getFileExtType());
businessFile.setBatchNo(bizNo);
businessFile.setSysCode(sysId);
businessFile.setFileSize(fileInfo.getTotalSize());
businessFile.setFilePath(fileInfo.getFileFullPath());
if(StringUtils.isNotEmpty(targetFileId)){
logger.info("************** 文件已经存在,根据已有文件ID更新数据库 - 开始 **************");
// 更新 business_file
businessFile.setFileId(targetFileId);
businessFile.setUpdatedBy(operaId);
businessFile.setUpdatedTime(currentDate);
QueryWrapper<BusinessFile> wrapper = new QueryWrapper<>();
wrapper.eq(TableColumnConstants.SYS_CODE,sysId)
.eq(TableColumnConstants.BATCH_NO,bizNo)
.eq(TableColumnConstants.FILE_ID,targetFileId);
int result = businessFileMapper.update(businessFile,wrapper);
if(result != 1){
dbOperMsg = "在已有文件上上传新版本失败,请重试!";
}
logger.info("************** 文件已经存在,文件ID更新数据库{}条数据,错误结果:{} - 结束 **************", result, dbOperMsg);
} else {
logger.info("************** 根据业务流id向数据库新增文件信息 - 开始:fileId: {};bizNo:{}; sysId:{} **************", fileId, bizNo, sysId);
businessFile.setFileId(fileId);
int insertResult = businessFileMapper.insert(businessFile);
RedisStatisticParams statisticParams = new RedisStatisticParams();
statisticParams.setOperName("文件统计-大文件切片组合上传-增加相关统计访问数");
statisticParams.setOperResult(insertResult);
statisticParams.setSysCode(businessFile.getSysCode());
statisticParams.setFileSize(businessFile.getFileSize());
statisticParams.setFileGroupType(FileUtil.getFileGroupType(businessFile.getFileType()));
StatisticUtils.increaseForFile(statisticParams);
logger.info("************** 根据业务流id向数据库新增文件信息 - 结束 **************");
}
BusinessFileVersion businessFileVersion =new BusinessFileVersion();
BeanUtils.copyProperties(businessFile, businessFileVersion);
businessFileVersion.setVersion(fileVer);
businessFileVersion.setVerFileId(fileId);
businessFileVersion.setCreateTime(new Date());
businessFileVersionMapper.insert(businessFileVersion);
logger.info("************** 根据业务流id及文件Id向数据库新增文件版本信息 **************");
return dbOperMsg;
}
/**
* 上传处理源文件,处理后上传
* 1.压缩文件 - 直接原文件压缩文件为zip,保存文件为一份
* 2.图片转缩略图 - 保留源文件,异步生成缩略图文件后存储
*/
private String dealWithOriginalFile(BusinessTrunckFileInfo uploadRq,BusinessFile businessFile,boolean isEncryption,String fileName, String path) throws Exception {
FileInputStream is=null;
FileOutputStream os=null;
try {
log.info("****************对图片进行处理,请求参数:{}****************", JSONObject.toJSONString(uploadRq));
String sysCode = uploadRq.getSysCode();
String bucket = uploadRq.getBucket();
String orgFileId = uploadRq.getFileId();
String fileNameSuffix = uploadRq.getFileExtType();
//当为mp4时,不做处理
if("mp4".equals(fileNameSuffix)){
return IsNoEnum.NO.getValue();
}
boolean isDealImage = ImageUtils.isImageThumbRel(fileNameSuffix,sysCode);
String isZipSave = accessSystemCache.getAccessSystemBySysId(sysCode).getIsZipSave();
boolean doZip = Constants.ZERO_STR.equals(isZipSave);
//当不是图片且不做zip处理,跳过判定
if(!isDealImage && !doZip){
return IsNoEnum.NO.getValue();
}
File file = new File(path);
is = new FileInputStream(new File(path));
byte[] bytes=new byte[is.available()];
is.read(bytes);
is.close();
if(isDealImage){
// 1.按接入系统配置的是否生成缩略图,生成压缩图片(上传文件本身已异步,此处不再异步生成)
byte[] smallImgByte = ImageUtils.getThumbImage(bytes, fileNameSuffix, Constants.IMAGE_SCALE);
String samllImgKey = commonFileService.getKey(sysCode, businessFile.getBatchNo(), orgFileId + Constants.THUMB_IMAGE_NAME_SUFFIX, fileNameSuffix);
boolean upSucc = uploadFile2Byte(smallImgByte, bucket, samllImgKey, isEncryption, businessFile.getSaveType()); // 上传缩略图
log.info("****************缩略图生成上传{}****************",upSucc?"成功":"失败");
return IsNoEnum.NO.getValue();
} else {
// 2.按接入系统配置的是否压缩,进行文件压缩上传 (图片已经生成缩略图,不再压缩)
byte[] zipBytes = ZipFileUtil.compressOneByZip(fileName, bytes);
file.delete();
os = new FileOutputStream(file);
os.write(zipBytes);
os.flush();
os.close();
return IsNoEnum.IS.getValue();
}
}catch (Exception e){
logger.error("文件下载异常 - 异常信息 \n{}",e);
throw e;
}finally {
if(is!=null){
is.close();
}
if(os!=null){
os.close();
}
}
}
/**
* 通过byte数组上传文件
* @param bytes
* @param bucket
* @param key
* @param isEncryption
* @param saveType - nas / ceph
* @return
*/
private boolean uploadFile2Byte(byte[] bytes,String bucket,String key,boolean isEncryption,String saveType){
log.info("*************** 以字节数组 {} 形式上传文件开始... ***************",saveType);
IBaseStorage storage = StorageFactory.createStorage(saveType);
boolean isUploadSucc = storage.uploadFile(bytes, bucket, key, isEncryption);
log.info("*************** 以字节数组 {} 形式上传文件结束,结果为:{}... ***************",saveType, isUploadSucc);
return isUploadSucc;
}
/**
* 切片传到ceph; 将临时合成文件删除
* @param fileInfo
* @return
*/
private boolean uploadFileToCeph(BusinessTrunckFileInfo fileInfo){
String sysCode = fileInfo.getSysCode();
String batchNo = fileInfo.getBatchNo();
String fileId = fileInfo.getFileId();
String saveType = Constants.STORAGE_TYPE_CEPH;
String tempFilePath = fileInfo.getNasTempFilePath();
boolean uploadSucc = uploadFileService.uploadFile2Binary(sysCode,batchNo,fileId,fileInfo.getFileExtType(),tempFilePath,saveType);
if(uploadSucc){
try {
Files.delete(Paths.get(tempFilePath));
} catch (IOException e) {
logger.error("删除临时文件失败!");
}
}
return uploadSucc;
}
public void checkParamValidation(BusinessUploadFileRq fileInfo){
String sysCode = fileInfo.getSysCode();
String batchNo = fileInfo.getBatchNo();
MultipartFile[] files = fileInfo.getBinaryFiles();
String filesSpecificInfo = fileInfo.getFilesSpecificInfo();
String validMsg = "";
if(StringUtils.isEmpty(sysCode)){
validMsg = "系统号不可为空";
} else if(StringUtils.isEmpty(batchNo)){
validMsg = "流水号不可为空";
} else if(files !=null && files.length == 0){
validMsg = "文件不可为空";
} else if(StringUtils.isNotEmpty(filesSpecificInfo) && !StringUtils.isJsonArrayString(filesSpecificInfo)){
validMsg = "文件附带属性不是Json形式的字符串数组";
}
if(StringUtils.isNotEmpty(validMsg)) {
throw new RuntimeException(validMsg + ",请更正后再试");
}
}
/**
* 处理二进制文件上传,仅上传文件
* @param fileInfo
* @return
*/
@Override
public BusinessUploadFileRs uploadFiles(BusinessUploadFileRq fileInfo) throws Exception {
checkParamValidation(fileInfo);
// 文件上传公共请求参数
String sysCode = fileInfo.getSysCode();
String batchNo = fileInfo.getBatchNo();
log.info("文件上传信息:{}, {}", sysCode, batchNo);
String createBy = fileInfo.getOperId();
String filesSpecificInfo = fileInfo.getFilesSpecificInfo();
if(StringUtils.isNotEmpty(filesSpecificInfo)){
JSONArray jsonArray = JSONObject.parseArray(filesSpecificInfo);
BusinessFilesSpecificInfoRq[] filesInfo = jsonArray.stream().map(specificFileInfo->{
String fileStringInfo = specificFileInfo.toString();
BusinessFilesSpecificInfoRq specificInfoRq = JSONObject.parseObject(fileStringInfo, BusinessFilesSpecificInfoRq.class);
return specificInfoRq;
}).toArray(BusinessFilesSpecificInfoRq[]::new);
fileInfo.setFileSpecificInfoArr(filesInfo);
}
BusinessFilesSpecificInfoRq[] fileSpecificInfoArr = fileInfo.getFileSpecificInfoArr();
CfgAccessSystem accessSystem = accessSystemCache.getAccessSystemBySysId(sysCode);
fileInfo.setAccessSystem(accessSystem);
String saveType = commonFileService.getStorageType();
String bucket = commonFileService.getBucket(sysCode,batchNo);
MultipartFile[] files = fileInfo.getBinaryFiles();
List<BusinessFile> uploadFiles = new ArrayList<>(files.length);
// 循环存储真实文件
int i = 0;
for(MultipartFile file : files) {
BusinessFile uploadFile = new BusinessFile();
String fileId = UUID.get32UUID();
Date nowTime = new Date();
byte[] fileBytes = file.getBytes();
long fileSize = file.getResource().contentLength();
String fileName = file.getOriginalFilename();
String fileSuffix = fileName.lastIndexOf(".") != -1 ? fileName.substring(fileName.lastIndexOf(".") + 1) : "";
String fileVer = commonFileService.getFileVersion(fileId);
String key = commonFileService.getKey(sysCode, batchNo, fileId, fileSuffix);
// 文件用户指定属性
Long orderId = fileSpecificInfoArr!=null && fileSpecificInfoArr.length>0? fileSpecificInfoArr[i].getOrderId():null;
String orgBillCode = fileSpecificInfoArr!=null && fileSpecificInfoArr.length>0? fileSpecificInfoArr[i].getBillCode():"";
String billCode = StringUtils.isNotEmpty(orgBillCode)?orgBillCode:Constants.noClassify;
String sceneBillCode = StringUtils.isNotEmpty(orgBillCode)?orgBillCode:""; // 特殊需要-场景-ocr识别使用
// 填充文件存储信息-开始
uploadFile.setSysCode(sysCode);
uploadFile.setBatchNo(batchNo);
uploadFile.setFileId(fileId);
uploadFile.setBucketId(bucket);
uploadFile.setBucketKey(key);
uploadFile.setFilePath(format("%s%s", bucket, key));
uploadFile.setBillCode(billCode);
uploadFile.setSceneBillCode(sceneBillCode);
uploadFile.setFileName(fileName);
uploadFile.setFileSize(fileSize);
uploadFile.setOrderId(orderId);
uploadFile.setFileType(fileSuffix);
uploadFile.setStatus(Constants.ZERO_STR);
uploadFile.setIsZipSave(accessSystem.getIsZipSave());
uploadFile.setSaveType(saveType);
uploadFile.setVersion(fileVer);
uploadFile.setCreatedBy(createBy);
uploadFile.setCreatedTime(nowTime);
uploadFile.setSrcFileByte(fileBytes);
uploadFiles.add(uploadFile);
// 填充文件存储信息-结束
i++;
}
fileInfo.setUploadFileInfoList(uploadFiles);
// 存储文件信息、及其他与文件相关的信息
BusinessUploadFileRs uploadFileRs = saveUploadFilesInfo(fileInfo);
return uploadFileRs;
}
/**
* 处理文件存储信息
* @param fileInfo
* @return
* @throws Exception
*/
@Override
public BusinessUploadFileRs saveUploadFilesInfo(BusinessUploadFileRq fileInfo){
List<BusinessFile> uploadFiles = fileInfo.getUploadFileInfoList();
BusinessUploadFileRs uploadFileRs = new BusinessUploadFileRs();
List<BusinessUploadFileInfoRs> uploadFilesInfoRs = new ArrayList<>();
// 存储流水号信息
saveUploadBatchInfo(fileInfo);
// 存储多文件信息
List<Future<BusinessUploadFileInfoRs>> responseFutures = new ArrayList<>();
AtomicInteger uploadSeq = new AtomicInteger(0);
for(BusinessFile file : uploadFiles) {
// 多文件同时上传上传、存储信息 - 为了多个文件异步上传
Future<BusinessUploadFileInfoRs> future = fileSaveExeService.submit(() -> saveUploadFileInfo(fileInfo, file, uploadSeq));
responseFutures.add(future);
}
uploadFilesInfoRs = responseFutures.stream().map(resFuture->{
BusinessUploadFileInfoRs uploadFileInfoRs = new BusinessUploadFileInfoRs();
try {
uploadFileInfoRs = resFuture.get();
} catch (InterruptedException | ExecutionException e) {
log.error("文件批量上传失败,线程中断:{}", e);
}
return uploadFileInfoRs;
}).collect(Collectors.toList());
uploadFileRs.setUploadFilesInfoRs(uploadFilesInfoRs);
return uploadFileRs;
}
/**
* 多文件同时上传上传、存储信息 - 为了多个文件异步上传
* @param fileInfo
* @param file
* @return
*/
private BusinessUploadFileInfoRs saveUploadFileInfo(BusinessUploadFileRq fileInfo,BusinessFile file,AtomicInteger uploadSeq){
// 公共定义参数
String sysCode = file.getSysCode();
String batchNo = file.getBatchNo();
String fileId = file.getFileId();
String token = fileInfo.getToken();
String operId = fileInfo.getOperId();
int fileUploadSeq = uploadSeq.get();
String ipWithPort = sysConfigCache.getConfigValueByConfigKey(SysConfigConstants.SYS_IP_PORT_NEW);
String filePreviewAddr = sysConfigCache.getConfigValueByConfigKey(SysConfigConstants.FILE_ONLINE_PREVIEW_ADDR);
CfgAccessSystem accessSystem = fileInfo.getAccessSystem();
List<BusinessUploadFileInfoRs> uploadFilesInfoRs = new ArrayList<>();
// 文件预览、下载拼接使用
TokenSecretParams tokenSecretParams = new TokenSecretParams();
BusinessOnlinePreviewParams previewParams = new BusinessOnlinePreviewParams();
BusinessFilesSpecificInfoRq[] fileSpecificInfoArr = fileInfo.getFileSpecificInfoArr();
// 文件上传响应信息
BusinessUploadFileInfoRs uploadFileInfoRs = new BusinessUploadFileInfoRs();
BeanUtils.copyProperties(file, uploadFileInfoRs); // 文件属性信息
BeanUtils.copyProperties(file, tokenSecretParams); // 文件下载信息
BeanUtils.copyProperties(file, previewParams); // 文件预览信息
log.info("上传文件-实际文件上传-开始:{}, {}, {}, {}", sysCode, batchNo, fileId, file.getFileName());
boolean uploadResult = uploadFileService.uploadFile2Bytes(file.getSrcFileByte(),sysCode,batchNo,fileId,file.getFileType());
log.info("上传文件-实际文件上传-结束,结果为:{}", uploadResult);
// 实际文件上传失败,不再进行文件存储,及文件其他操作处理
if(!uploadResult){
uploadFileInfoRs.setUploadTrueFileSucc(false);
uploadFileInfoRs.setUploadFileInfoSucc(false);
return uploadFileInfoRs;
}
tokenSecretParams.setToken(token);
tokenSecretParams.setOperId(operId); // 下载当前操作人
String fileDownloadUrl = Constants.HTTP + ipWithPort + FileTokenUtils.getDownloadPath(tokenSecretParams); // 二进制上传-原文件地址
String imageThumbPath = null;
previewParams.setFilePath(fileDownloadUrl); // 预览-下载地址
file.setFileDownloadUrl(fileDownloadUrl); // ocr识别-下载地址
if(ImageUtils.isImageThumbRel(file.getFileType(),sysCode)) {
tokenSecretParams.setThumbDownload(true);
imageThumbPath = Constants.HTTP + ipWithPort + FileTokenUtils.getDownloadPath(tokenSecretParams); // 二进制上传-缩略图地址
}
log.info("上传文件-文件信息存储-开始");
int fileInsRs = businessFileMapper.insert(file);
BusinessFileVersion fileVersion = new BusinessFileVersion();
BeanUtils.copyProperties(file, fileVersion);
fileVersion.setVerFileId(file.getFileId());
fileVersion.setCreateTime(new Date());
int fileVerInsRs = businessFileVersionMapper.insert(fileVersion);
log.info("上传文件-文件信息存储-结束:{}", fileVerInsRs);
log.info("上传文件-图片OCR识别-开始");
updateFileOcrType(file, fileVersion);
log.info("上传文件-图片OCR识别-结束:{}", file.getBillCode());
// 存储标签、关联文件标签信息 - 若有警示信息,仅起到警示作用不一一返回,做统一返回
if(fileSpecificInfoArr != null && fileSpecificInfoArr.length != 0) {
log.info("上传文件-添加关联标签-开始");
String addMetadataWarnMsg = saveFileMetadataInfo(file, fileSpecificInfoArr[fileUploadSeq]);
uploadFileInfoRs.setUploadFileTagWarnMsg(addMetadataWarnMsg);
log.info("上传文件-添加关联标签-结束:{}", addMetadataWarnMsg);
}
log.info("上传文件-文件保存返回信息-开始");
uploadFileInfoRs.setUploadTrueFileSucc(uploadResult);
uploadFileInfoRs.setUploadFileInfoSucc(fileInsRs>0 && fileVerInsRs>0?true:false);
uploadFileInfoRs.setFileDownloadUrl(fileDownloadUrl);
uploadFileInfoRs.setImageThumbPath(imageThumbPath);
previewParams.setCurrentSystemName(accessSystem.getSysName());
previewParams.setCurrentUser(operId);
previewParams.setPreviewFileNo(fileId+sysCode+batchNo); // 预览-文件缓存名称
uploadFileInfoRs.setFilePreviewUrl(filePreviewAddr + FileTokenUtils.getPreviewSecretParams(previewParams)); // 批量binary-上传返回
uploadFilesInfoRs.add(uploadFileInfoRs);
log.info("上传文件-文件上传返回信息-结束:{}", JSONObject.toJSONString(uploadFileInfoRs));
log.info("上传文件-上传、保存第 {} 个文件成功 ----------------------", uploadSeq.get()+1); // 起始角标为0
uploadSeq.getAndIncrement();
// TODO 机构号 柜员号
EsBusinessFileEntity esBusinessFileEntity = new EsBusinessFileEntity(file.getFileId()
, file.getSysCode(), null, file.getBatchNo(), file.getFileName(), file.getFileType(),
file.getBillCode(), file.getCreatedBy(), fileVersion.getCreateTime(), null, null, null);
esFileService.addOrUpdEsFile(esBusinessFileEntity);
return uploadFileInfoRs;
}
/**
* 处理流水号信息
* @param fileInfo
* @return
* @throws Exception
*/
@Override
public void saveUploadBatchInfo(BusinessUploadFileRq fileInfo){
QueryWrapper qw = new QueryWrapper();
qw.eq(TableColumnConstants.SYS_CODE, fileInfo.getSysCode());
qw.eq(TableColumnConstants.BATCH_NO, fileInfo.getBatchNo());
int cnt = businessBatchMapper.selectCount(qw);
if(cnt == 0){
BusinessBatch batch = new BusinessBatch();
BeanUtils.copyProperties(fileInfo,batch);
batch.setStatus(Constants.ZERO_STR);
batch.setCreatedTime(new Date());
log.info("上传文件-添加流水号请求参数为:{}", JSONObject.toJSONString(fileInfo.getFilesSpecificInfo()));
businessBatchMapper.insert(batch);
}
}
/**
* 处理标签及文件与标签关联关系
* @param businessFile
* @param fileInfoRq
* @return
*/
@Override
public String saveFileMetadataInfo(BusinessFile businessFile, BusinessFilesSpecificInfoRq fileInfoRq){
String ownerSysCode = businessFile.getSysCode();
String batchNo = businessFile.getBatchNo();
String createBy = businessFile.getCreatedBy();
List<BusinessMetadataDto> metadatas = fileInfoRq.getMetadatas();
// 为一个文件添加多个标签 - 已有标签,未有标签,标签关联文件
BusinessMetadataInfoRq metadata = new BusinessMetadataInfoRq();
metadata.setSysCode(ownerSysCode);
metadata.setBatchNo(batchNo);
metadata.setFileId(businessFile.getFileId());
metadata.setCreateBy(createBy);
metadata.setMetadatas(metadatas);
String warningMsg = businessMetadataService.addMetadataForFile(metadata);
return warningMsg;
}
/**
* 处理文件自动分类。上传文件后,更新文件分类 - 获取OCR识别类型 - 一个文件
* @param businessFile 更新文件 - 分类仅存在于最新表中
* @param businessFileVersion 校验文件可否下载 - 防止历史表数据未生成,下载文件都走历史表
*/
@Override
public void updateFileOcrType(BusinessFile businessFile, BusinessFileVersion businessFileVersion) {
String fileId = businessFile.getFileId();
String billType = businessFile.getBillCode();
String sceneBillType = businessFile.getSceneBillCode();
Long fileInsertId = businessFileVersion.getId();
CfgAccessSystem accessSystem = accessSystemCache.getAccessSystemBySysId(businessFile.getSysCode());
boolean needOcrCheck = Constants.ZERO_STR.equals(accessSystem.getNeedOcrIdentified()) ? true : false;
boolean isImage = ImageUtils.isImage(businessFile.getFileType());
if (!isImage || !needOcrCheck || fileInsertId == null) {
log.info("**********未进行ocr识别!此文件是否是图片:{};此系统是否需要OCR识别:{};历史文件ID:[{}];***********",
isImage, needOcrCheck, fileInsertId);
return;
}
log.info("**********文件插入后的文件ID:{};文件信息:{}***********", fileInsertId, JSONObject.toJSONString(businessFile));
// ocr识别
String fileDownloadPath = businessFile.getFileDownloadUrl(); // 已存在文件下载地址
IdentifiedPicRq identifiedPicRq = new IdentifiedPicRq();
List<IdentifiedPicInfoRq> dataList = new ArrayList<>();
IdentifiedPicInfoRq picInfoRq = new IdentifiedPicInfoRq(fileId, fileDownloadPath);
picInfoRq.setFileBillCode(billType);
dataList.add(picInfoRq);
identifiedPicRq.setData_list(dataList);
identifiedPicRq.setOcrIdentifiedUri(ExtendsConfiguration.ocrIdentifiedUri);
// 仅识别一个文件 - 多文件识别时,时间过长
List<String> billCodes = ImageUtils.identifiedPicBillCode(needOcrCheck, identifiedPicRq);
String oneFileOcrBillCode = billCodes != null && billCodes.size() > 0 ? (StringUtils.isNotEmpty(billCodes.get(0))?billCodes.get(0):""):"";
boolean needCombineOcrRes = StringUtils.isNotEmpty(sceneBillType)
&& StringUtils.isNotEmpty(oneFileOcrBillCode) ? true : false;
billType = needCombineOcrRes? sceneBillType +"-"+ oneFileOcrBillCode :
StringUtils.isNotEmpty(oneFileOcrBillCode)? oneFileOcrBillCode :
StringUtils.isNotEmpty(billType)? billType : Constants.NO_CLASSIFY;
QueryWrapper qw = new QueryWrapper();
qw.eq(TableColumnConstants.BATCH_NO, businessFile.getBatchNo());
qw.eq(TableColumnConstants.SYS_CODE, businessFile.getSysCode());
qw.eq(TableColumnConstants.FILE_ID, businessFile.getFileId());
businessFile.setBillCode(billType);
businessFile.setUpdatedBy(businessFile.getCreatedBy());
businessFile.setUpdatedTime(new Date());
businessFileMapper.update(businessFile, qw); // 文件分类仅存在于最新文件下,无需更新历史列表
esFileService.updEsFileBillCode(fileId,billType);
}
}
package com.northking.core.service.business.impl;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.northking.common.domain.entity.business.BusinessFile;
import com.northking.common.domain.entity.business.BusinessTrunckInfo;
import com.northking.common.domain.po.business.BusinessTrunckResult;
import com.northking.common.mapper.BusinessTrunckInfoMapper;
import com.northking.common.utils.StringUtils;
import com.northking.core.service.business.BusinessSystemService;
import com.northking.core.service.business.BusinessTrunkInfoService;
import com.northking.core.service.file.CommonFileService;
import com.northking.core.utils.FileInfoUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
@Service
public class BusinessTrunkInfoServiceImpl extends ServiceImpl<BusinessTrunckInfoMapper, BusinessTrunckInfo>
implements BusinessTrunkInfoService {
@Autowired
private BusinessTrunckInfoMapper chunkInfoMapper;
@Autowired
private CommonFileService commonFileService;
@Autowired
private BusinessSystemService businessSystemService;
protected final Logger log= LoggerFactory.getLogger(BusinessTrunkInfoServiceImpl.class);
/**
* 校验当前文件
* @param chunkInfo
* @return 秒传?续传?新传?
*/
@Override
public BusinessTrunckResult checkChunkState(BusinessTrunckInfo chunkInfo) {
log.info("******************文件切片上传参数校验{}*********"+ JSONObject.toJSONString(chunkInfo));
BusinessFile businessFile = new BusinessFile();
businessFile.setMd5(chunkInfo.getIdentifier());
businessFile.setSysCode(chunkInfo.getSysCode());
businessFile.setBatchNo(chunkInfo.getBatchNo());
businessFile.setSaveType(commonFileService.getStorageType());
BusinessFile businessFileRs = businessSystemService.doesFileTrulyExist(businessFile);
BusinessTrunckResult chunkResult = new BusinessTrunckResult();
// 判断是否有MD5相同的文件 - 同一个流水号,同一系统,同一存储
if(StringUtils.isNotEmpty(businessFileRs.getMd5())){
chunkResult.setSkipUpload(true);
chunkResult.setLocation(businessFileRs.getFilePath());
chunkResult.setMessage("完整文件已存在,直接跳过上传,实现秒传");
log.info("*************秒上传文件成功**************");
return chunkResult;
}
// 查找块文件,传递到哪个块
ArrayList<Integer> list = chunkInfoMapper.selectChunkNumbers(chunkInfo);
if (list !=null && list.size() > 0) {
chunkResult.setSkipUpload(false);
chunkResult.setUploadedChunks(list);
chunkResult.setMessage("部分文件块已存在,继续上传剩余文件块,实现断点续传");
return chunkResult;
}
log.info("******************断点续传文件切片上传成功*********");
return chunkResult;
}
/**
* 写文件
* @param chunk
* @return
*/
@Override
public Integer uploadFile(BusinessTrunckInfo chunk) {
log.info("*************************文件传参数校验{}**********************"+JSONObject.toJSONString(chunk));
Integer apiRlt = HttpServletResponse.SC_OK;
MultipartFile file = chunk.getBinaryFile();
try {
byte[] bytes = file.getBytes();
// 将块文件临时写到nas上
String fileMd5 = chunk.getIdentifier();
String fileName = chunk.getFilename();
String fileType = StringUtils.isNotEmpty(fileName) && fileName.lastIndexOf(".")>0?fileName.substring(fileName.lastIndexOf(".")+1):"";
String uploadFolder = commonFileService.getChunkBucket(chunk.getSysCode(),chunk.getBatchNo(),fileMd5);
String uploadFile = commonFileService.getChunkKey(fileMd5,fileType,chunk.getChunkNumber().toString());
Path path = Paths.get(FileInfoUtils.generatePath(uploadFolder, uploadFile));
log.info("*****************获取文件路径*******************"+path);
Files.write(path, bytes);
chunk.setCreateTime(new Date());
if(chunkInfoMapper.insert(chunk) < 0){
apiRlt = HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
}
} catch (IOException e) {
log.error("写文件出错:{}"+e.getMessage());
apiRlt = HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
}
return apiRlt;
}
}
3、公共读写方法
/**
* 将多个图片合成一个PDF文件
* @param imagesMap 图片文件数组
* @param pdfOutputStream PDF文件保存地址
*
*/
public static void writeImagesToPdf(Map<BusinessFile,InputStream> imagesMap, OutputStream pdfOutputStream) throws Exception{
//创建一个文档对象
Document doc = new Document(null, 0, 0, 0, 0);
try {
// 创建输出文件的位置
PdfWriter.getInstance(doc, pdfOutputStream);
Set<Map.Entry<BusinessFile, InputStream>> entries = imagesMap.entrySet();
for (Map.Entry<BusinessFile, InputStream> entry : entries) {
InputStream imageIS=null;
try {
doc.newPage(); // 一个图片一页
BusinessFile file = entry.getKey();
imageIS = entry.getValue();
int available = imageIS.available();
byte[] bytes=new byte[available];
imageIS.read(bytes);
if(IsNoEnum.IS.getValue().equals(file.getIsZipSave())){
//进行解压处理
bytes = ZipFileUtil.decompressOneByZip(bytes);
}
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
BufferedImage bufferedImg = ImageIO.read(is);
doc.setPageSize(new com.lowagie.text.Rectangle(bufferedImg.getWidth(), bufferedImg.getHeight()));
com.lowagie.text.Image image = com.lowagie.text.Image.getInstance(bytes);
//开启文档
doc.open();
doc.add(image);
}catch (Exception e){
log.error("图片转pdf异常 - Exception 异常信息:{};\n异常问题路径:",e.getMessage(),e.getStackTrace());
throw e;
}finally {
if(imageIS!=null){
imageIS.close();
}
}
}
// 关闭文档
doc.close();
} catch (IOException e) {
log.error("图片转pdf异常 - IOException 异常信息:{};\n异常问题路径:",e.getMessage(),e.getStackTrace());
} catch (BadElementException e) {
log.error("图片转pdf异常 - BadElementException 异常信息:{};\n异常问题路径:",e.getMessage(),e.getStackTrace());
} catch (DocumentException e) {
log.error("图片转pdf异常 - DocumentException 异常信息:{};\n异常问题路径:",e.getMessage(),e.getStackTrace());
} finally {
if(doc != null) {
doc.close();
log.info("图片转pdf - Finally最终处理 :关闭PDF文件");
}
if(pdfOutputStream != null){
try {
pdfOutputStream.close();
} catch (IOException e) {
log.error("图片转pdf异常 - Finally关闭文件流-异常信息:{};\n异常问题路径:",e.getMessage(),e.getStackTrace());
}
}
}
}
/**
* 使用ZIP算法进行压缩.
* @param sourceFileBytesMap 待压缩文件的Map集合.
* @return 压缩后的ZIP文件字节数组.
* @throws Exception 压缩过程中可能发生的异常,若发生异常,则返回的字节数组长度为0.
*/
public static byte[] compressByZip(Map<String, byte[]> sourceFileBytesMap) throws Exception {
// 变量定义.
ZipEntry zipEntry = null;
ZipOutputStream zipZos = null;
ByteArrayOutputStream zipBaos = null;
try {
// 压缩文件变量初始化.
zipBaos = new ByteArrayOutputStream();
zipZos = new ZipOutputStream(zipBaos);
// 将文件添加到ZIP条目中.
if (sourceFileBytesMap != null && sourceFileBytesMap.size() > 0) {
for (Map.Entry<String, byte[]> singleFile : sourceFileBytesMap.entrySet()) {
zipEntry = new ZipEntry(singleFile.getKey());
zipZos.putNextEntry(zipEntry);
zipZos.write(singleFile.getValue());
}
} else {
zipBaos = new ByteArrayOutputStream();
}
} finally {
if (zipBaos != null)
zipBaos.close();
if (zipZos != null)
zipZos.close();
}
return zipBaos.toByteArray();
}
/**
* 使用ZIP算法进行解压.
* @param sourceZipFileBytes ZIP单文件字节数组.
* @return 解压后的单文件字节数组
* @throws Exception 解压过程中可能发生的异常,若发生异常抛异常
*/
public static byte[] decompressOneByZip(byte[] sourceZipFileBytes) throws Exception {
Map<String, byte[]> fileMap = ZipFileUtil.decompressByZip(sourceZipFileBytes);
//若结果为空返回空字节数组,若结果不为空,返回第一个字节数组
if(fileMap ==null || fileMap.size()==0)
return new byte[0];
Map.Entry<String, byte[]> next=fileMap.entrySet().iterator().next();
return next.getValue();
}
/**
* 按照固定比例生成缩略图
* @param imageOrgBytes
* @param imageOrgType
* @return
*/
public static byte[] getThumbImage(byte[] imageOrgBytes, String imageOrgType, double imageScale){
try {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ByteArrayInputStream inputStream = new ByteArrayInputStream(imageOrgBytes);
Image image = ImageIO.read(inputStream);
int width = image.getWidth(null); //获取原图宽度
int height = image.getHeight(null);//获取原图高度
BigDecimal bigDecimalWidth = new BigDecimal(width);
BigDecimal bigDecimalHeight = new BigDecimal(height);
BigDecimal scale = new BigDecimal(imageScale);
int newWidth = bigDecimalWidth.multiply(scale).intValue();
int newHeight = bigDecimalHeight.multiply(scale).intValue();
BufferedImage bufferedImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
//图片缩略图实现
bufferedImage.getGraphics().drawImage(image.getScaledInstance(newWidth, newHeight, image.SCALE_SMOOTH), 0, 0, null);
ImageIO.write(bufferedImage, imageOrgType, bao);
String thumbBaseStr = Base64.getEncoder().encodeToString(bao.toByteArray());
byte[] thumbImgBytes = Base64.getDecoder().decode(thumbBaseStr);
return thumbImgBytes;
} catch (IOException e) {
log.error("按照高度和宽度生成缩略图失败{}", e);
return null;
}
}
/**
* 创建路径
* @param uploadFolder
* @param fileName
* @return 文件
*/
public static String generatePath(String uploadFolder, String fileName) {
StringBuilder sb = new StringBuilder();
sb.append(uploadFolder);
//判断uploadFolder/identifier 路径是否存在,不存在则创建
if (!Files.isWritable(Paths.get(sb.toString()))) {
log.info("路径不存在,新建路径: {}", sb.toString());
try {
Files.createDirectories(Paths.get(sb.toString()));
} catch (IOException e) {
log.error("创建路径错误:{},{}",e.getMessage(), e);
}
}
return sb.append(fileName).toString();
}
/**
* 合并文件
* @param file
* @param folder
* @param filename
* @return 状态
*/
public static Integer merge(String file, String folder, String filename){
//默认合并成功
Integer rlt = HttpServletResponse.SC_OK;
try {
//不存在的话,进行合并
Files.createFile(Paths.get(file));
Files.list(Paths.get(folder))
.filter(path -> !path.getFileName().toString().equals(filename))
.sorted((o1, o2) -> {
String p1 = o1.getFileName().toString();
String p2 = o2.getFileName().toString();
int i1 = p1.lastIndexOf("-");
int i2 = p2.lastIndexOf("-");
return Integer.valueOf(p2.substring(i2)).compareTo(Integer.valueOf(p1.substring(i1)));
})
.forEach(path -> {
try {
//以追加的形式写入文件
Files.write(Paths.get(file), Files.readAllBytes(path), StandardOpenOption.APPEND);
//合并后删除该块
Files.delete(path);
} catch (IOException e) {
log.error("删除文件失败:{},{}",e.getMessage(), e);
}
});
} catch (IOException e) {
log.error("合并失败:{},{}",e.getMessage(), e);
//合并失败
rlt = HttpServletResponse.SC_BAD_REQUEST;
}
return rlt;
}
/**
* 移動文件
*/
public static boolean move(String originalFile,String targetFileDirectory,String targetFileName) {
log.info("move start...");
boolean moveSucc = false;
try {
Path orgFile = Paths.get(originalFile);
Path tarPath = Paths.get(targetFileDirectory);
if(!Files.exists(tarPath)){
Files.createDirectories(tarPath);
}
Path tarFile = Paths.get(tarPath + "/" + targetFileName);
Path path = Files.move(orgFile,tarFile,StandardCopyOption.REPLACE_EXISTING);
if(path!=null && path.getFileName()!=null && StringUtils.isNotEmpty(path.getFileName().toString())){
moveSucc = true;
}
} catch (IOException e) {
log.error("文件移动到目标NAS路径失败!\n"+e.getMessage()+"\n"+e.getStackTrace());
e.printStackTrace();
}
log.info("move end...");
return moveSucc;
}
/**
* 图片添加水印 - 图片、文字、图片和文字
*
* @param waterMarVo 水印实体参数类
* @return 添加完水印的图片的base64字符串
*/
public static byte[] imageAddWaterMark(WaterMarVo waterMarVo) throws Exception{
log.info("Enter ImageAddWaterMarkService.imageAddWaterMark and waterMarVo is "+ waterMarVo.toString());
byte[] srcImageByte = waterMarVo.getSrcImageBytes();
byte[] returnBytes = null;
//如果为添加文字
if (ImageProcessConstant.WATER_MARK_TEXT.equals(waterMarVo.getType())
|| ImageProcessConstant.WATER_MARK_ALL.equals(waterMarVo.getType())) {
returnBytes = addTextWaterMark(srcImageByte, waterMarVo);
}
String picImageMarkBase = waterMarVo!=null && waterMarVo.getWaterMarPicVo()!=null?waterMarVo.getWaterMarPicVo().getPicImageBase():"";
if (ImageProcessConstant.WATER_MARK_PIC.equals(waterMarVo.getType()) && StringUtils.isNotEmpty(picImageMarkBase)) {
byte[] picWaterMark = Base64.getDecoder().decode(picImageMarkBase);
returnBytes = addPicWaterMark(srcImageByte, waterMarVo, picWaterMark);
}
if (ImageProcessConstant.WATER_MARK_ALL.equals(waterMarVo.getType()) && returnBytes != null && StringUtils.isNotEmpty(picImageMarkBase)) {
byte[] picWaterMark = Base64.getDecoder().decode(picImageMarkBase);
returnBytes = addPicWaterMark(returnBytes, waterMarVo, picWaterMark);
}
log.info("Exit ImageAddWaterMarkService.imageAddWaterMark result length is " + returnBytes.length);
return returnBytes;
}
/**
* 添加图片水印
* @param srcImageBytes
* @param waterMarVo
* @param picImageBytes
* @return
* @throws IOException
*/
public static byte[] addPicWaterMark(byte[] srcImageBytes, WaterMarVo waterMarVo, byte[] picImageBytes) throws IOException {
WaterMarPicVo waterMarPicVo = waterMarVo.getWaterMarPicVo();
try (ByteArrayInputStream srcStream=new ByteArrayInputStream(srcImageBytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
Image image=ImageIO.read(srcStream);
int imageWidth = image.getWidth(null);
int imageHeight = image.getHeight(null);
double rotate = UipConfig.getWatermarkRotate()!=0?UipConfig.getWatermarkRotate():-10;
BufferedImage bufferedImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = bufferedImage.createGraphics();
/**设置对线段的锯齿状边缘处理**/
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.drawImage(image.getScaledInstance(imageWidth, imageHeight, image.SCALE_SMOOTH), 0, 0, null);
if (ImageProcessConstant.ALL.equals(waterMarPicVo.getLocation())) {
/**设置水印旋转**/
graphics2D.rotate(Math.toRadians(rotate));
}
graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
waterMarPicVo.getAlpha() == 0 ? 1.0f : waterMarPicVo.getAlpha()));
//处理水印图片
ImageIcon imageIcon=new ImageIcon(picImageBytes);
Image stampImage=imageIcon.getImage();
addPic(waterMarPicVo.getLocation(), imageWidth, imageHeight, stampImage, graphics2D);
graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
graphics2D.dispose();
ImageIO.write(bufferedImage, waterMarVo.getFileType(), baos);
return baos.toByteArray();
} catch (IOException e) {
throw new IOException(e);
}
}
/**
* 添加文字水印
* **** 若汉字乱码,则需要在java.home路径的lib文件夹下放置fonts字体,并重启服务生效 ****
* @param srcBytes
* @param waterMarVo
* @return
*/
public static byte[] addTextWaterMark(byte[] srcBytes, WaterMarVo waterMarVo) throws IOException {
WaterMarTextVo waterMarTextVo = waterMarVo.getWaterMarTextVo();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ByteArrayInputStream srcStream = new ByteArrayInputStream(srcBytes);
try {
Image image = ImageIO.read(srcStream);
int imageWidth = image.getWidth(null);
int imageHeight = image.getHeight(null);
String fontFamily = StringUtils.isNotEmpty(waterMarTextVo.getFontFamily())?waterMarTextVo.getFontFamily():"微软雅黑";
double rotate = UipConfig.getWatermarkRotate()!=0?UipConfig.getWatermarkRotate():-10;
BufferedImage bufferedImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = bufferedImage.createGraphics();
graphics2D.drawImage(image, 0, 0, imageWidth, imageHeight, null);
// 字体与预览项目配置同步
Font font = new Font(fontFamily, Font.PLAIN, waterMarTextVo.getTextSize() == 0 ? 24 : waterMarTextVo.getTextSize());
graphics2D.setColor(waterMarTextVo.getTextColor() == null ? Color.gray : waterMarTextVo.getTextColor());
graphics2D.setFont(font);
int length = getWatermarkLength(waterMarTextVo.getText(), graphics2D);
//当铺满全屏时有旋转
if (ImageProcessConstant.ALL.equals(waterMarTextVo.getLocation())) {
//设置水印旋转
graphics2D.rotate(Math.toRadians(rotate));
}
//设置透明度
graphics2D.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,
waterMarTextVo.getAlpha() == 0 ? 1.0f : waterMarTextVo.getAlpha()));
//添加水印
addText(waterMarTextVo, graphics2D, length, imageWidth, imageHeight, font);
graphics2D.dispose();
ImageIO.write(bufferedImage, waterMarVo.getFileType(), baos);
return baos.toByteArray();
} catch (IOException e) {
throw new IOException(e);
}
}
/**
* 获取字体的长度
*
* @param waterMarkContent
* @param graphics2D
* @return
*/
private static int getWatermarkLength(String waterMarkContent, Graphics2D graphics2D) {
return graphics2D.getFontMetrics(graphics2D.getFont()).charsWidth(waterMarkContent.toCharArray(), 0, waterMarkContent.length());
}
/**
* 根据不同位置添加文字水印
*
* @param waterMarTextVo
* @param graphics2D
* @param length
* @param imageWidth
* @param font
* @param imageHeight
*/
private static void addText(WaterMarTextVo waterMarTextVo, Graphics2D graphics2D, int length, int imageWidth, int imageHeight, Font font) {
//默认左上
int x = 20;
int y = 30;
String markTxt = waterMarTextVo.getText();
switch (waterMarTextVo.getLocation()) {
/*case ImageProcessConstant.TOP_LEFT:
x = 20;
y = 30;
break;*/
case ImageProcessConstant.TOP_CENTER:
x = (imageWidth - length) / 2;
y = 30;
break;
case ImageProcessConstant.TOP_RIGHT:
x = imageWidth - length - 20;
y = 30;
break;
case ImageProcessConstant.BOTTOM_LEFT:
x = 20;
y = imageHeight - 30;
break;
case ImageProcessConstant.BOTTOM_CENTER:
x = (imageWidth - length) / 2;
y = imageHeight - 30;
break;
case ImageProcessConstant.BOTTOM_RIGHT:
x = imageWidth - length - 20;
y = imageHeight - 30;
break;
case ImageProcessConstant.CENTER:
x = (imageWidth - length) / 2;
y = imageHeight / 2;
break;
}
if (!ImageProcessConstant.ALL.equals(waterMarTextVo.getLocation())) {
graphics2D.drawString(markTxt, x, y);
} else {
// 整铺
JLabel label = new JLabel(markTxt);
FontMetrics metrics = label.getFontMetrics(font);
int width = metrics.stringWidth(label.getText());//文字水印的宽
int height = metrics.getHeight();
int rowsNumber = imageHeight / height;// 图片的高 除以 文字水印的宽 ——> 打印的行数(以文字水印的宽为间隔)
int columnsNumber = imageWidth / width;//图片的宽 除以 文字水印的宽 ——> 每行打印的列数(以文字水印的宽为间隔)
//防止图片太小而文字水印太长,所以至少打印一次
if (rowsNumber < 1) {
rowsNumber = 1;
}
if (columnsNumber < 1) {
columnsNumber = 1;
}
double blankRows = UipConfig.getWatermarkBlankRows()!=0?UipConfig.getWatermarkBlankRows():1.5;
for (int j = 0; j < rowsNumber; j++) {
for (int i = 0; i < columnsNumber; i++) {
BigDecimal xIdxOrg = new BigDecimal(i * width + i * height);
BigDecimal yIdxOrg = new BigDecimal(j * height + j * height);
int xIdx = (xIdxOrg.multiply(new BigDecimal(Double.toString(blankRows)))).intValue();
int yIdx = (yIdxOrg.multiply(new BigDecimal(Double.toString(blankRows)))).intValue();
graphics2D.drawString(markTxt, xIdx, yIdx);
}
}
}
}
/**
* 添加图片水印 默认左上
*
* @param location
* @param srcWidth
* @param srcHeight
* @param stampImage
* @param graphics2D
*/
private static void addPic(String location, int srcWidth, int srcHeight, Image stampImage, Graphics graphics2D) {
int imarkWidth = stampImage.getWidth(null);
int imarkHeight = stampImage.getHeight(null);
int x = 0;
int y = 0;
switch (location) {
/*case ImageProcessConstant.TOP_LEFT:
x = 0;
y = 0;
break;*/
case ImageProcessConstant.TOP_CENTER:
x = (srcWidth - imarkWidth) / 2;
y = 0;
break;
case ImageProcessConstant.TOP_RIGHT:
x = srcWidth - imarkWidth;
y = 0;
break;
case ImageProcessConstant.BOTTOM_LEFT:
x = 0;
y = srcHeight - imarkHeight;
break;
case ImageProcessConstant.BOTTOM_CENTER:
x = (srcWidth - imarkWidth) / 2;
y = srcHeight - imarkHeight;
break;
case ImageProcessConstant.BOTTOM_RIGHT:
x = srcWidth - imarkWidth;
y = srcHeight - imarkHeight;
break;
case ImageProcessConstant.CENTER:
x = (srcWidth - imarkWidth) / 2;
y = (srcHeight - imarkHeight) / 2;
break;
}
if (!ImageProcessConstant.ALL.equals(location)) {
graphics2D.drawImage(stampImage, x, y, null);
} else {
// 整铺
int rowsNumber = srcHeight / imarkHeight;// 图片的高 除以 文字水印的宽 ——> 打印的行数(以文字水印的宽为间隔)
int columnsNumber = srcWidth / imarkWidth;//图片的宽 除以 文字水印的宽 ——> 每行打印的列数(以文字水印的宽为间隔)
//防止图片太小而文字水印太长,所以至少打印一次
if (rowsNumber < 1) {
rowsNumber = 1;
}
if (columnsNumber < 1) {
columnsNumber = 1;
}
for (int j = 0; j < rowsNumber; j++) {
for (int i = 0; i < columnsNumber; i++) {
graphics2D.drawImage(stampImage, i * imarkWidth + i * imarkHeight, j * imarkHeight + j * imarkHeight, null);
}
}
}
}
/**
* 将多个图片合成一个PDF文件
* @param imagesMap 图片文件数组
* @param pdfOutputStream PDF文件保存地址
*
*/
public static void writeImagesToPdf(Map<BusinessFile,InputStream> imagesMap, OutputStream pdfOutputStream) throws Exception{
//创建一个文档对象
Document doc = new Document(null, 0, 0, 0, 0);
try {
// 创建输出文件的位置
PdfWriter.getInstance(doc, pdfOutputStream);
Set<Map.Entry<BusinessFile, InputStream>> entries = imagesMap.entrySet();
for (Map.Entry<BusinessFile, InputStream> entry : entries) {
InputStream imageIS=null;
try {
doc.newPage(); // 一个图片一页
BusinessFile file = entry.getKey();
imageIS = entry.getValue();
int available = imageIS.available();
byte[] bytes=new byte[available];
imageIS.read(bytes);
if(IsNoEnum.IS.getValue().equals(file.getIsZipSave())){
//进行解压处理
bytes = ZipFileUtil.decompressOneByZip(bytes);
}
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
BufferedImage bufferedImg = ImageIO.read(is);
doc.setPageSize(new com.lowagie.text.Rectangle(bufferedImg.getWidth(), bufferedImg.getHeight()));
com.lowagie.text.Image image = com.lowagie.text.Image.getInstance(bytes);
//开启文档
doc.open();
doc.add(image);
}catch (Exception e){
log.error("图片转pdf异常 - Exception 异常信息:{};\n异常问题路径:",e.getMessage(),e.getStackTrace());
throw e;
}finally {
if(imageIS!=null){
imageIS.close();
}
}
}
// 关闭文档
doc.close();
} catch (IOException e) {
log.error("图片转pdf异常 - IOException 异常信息:{};\n异常问题路径:",e.getMessage(),e.getStackTrace());
} catch (BadElementException e) {
log.error("图片转pdf异常 - BadElementException 异常信息:{};\n异常问题路径:",e.getMessage(),e.getStackTrace());
} catch (DocumentException e) {
log.error("图片转pdf异常 - DocumentException 异常信息:{};\n异常问题路径:",e.getMessage(),e.getStackTrace());
} finally {
if(doc != null) {
doc.close();
log.info("图片转pdf - Finally最终处理 :关闭PDF文件");
}
if(pdfOutputStream != null){
try {
pdfOutputStream.close();
} catch (IOException e) {
log.error("图片转pdf异常 - Finally关闭文件流-异常信息:{};\n异常问题路径:",e.getMessage(),e.getStackTrace());
}
}
}
}