package com.vci.web.service.impl; import com.vci.corba.common.PLException; import com.vci.corba.volume.VolumeServicePrx; import com.vci.file.constant.VciFileDefaultValueConstant; import com.vci.file.constant.VciFileLangCodeConstant; import com.vci.file.dto.VciFileObjectDTO; import com.vci.file.enumpck.VciFileTransProtocolEnum; import com.vci.file.lcstatuspck.FileLCStatus; import com.vci.file.pagemodel.VciFileObjectVO; import com.vci.frameworkcore.constant.FrameWorkLangCodeConstant; import com.vci.starter.revision.service.RevisionModelUtil; import com.vci.starter.web.annotation.bus.VciChangeDocument; import com.vci.starter.web.enumpck.DataSecretEnum; import com.vci.starter.web.enumpck.UserSecretEnum; import com.vci.starter.web.exception.VciBaseException; import com.vci.starter.web.pagemodel.BaseResult; import com.vci.starter.web.util.BeanUtil; import com.vci.starter.web.util.LocalFileUtil; import com.vci.starter.web.util.VciBaseUtil; import com.vci.starter.web.wrapper.VciQueryWrapperForDO; import com.vci.web.constant.WebLangCodeConstant; import com.vci.web.dao.VciFileDocClassifyDaoI; import com.vci.web.dao.VciFileObjectDaoI; import com.vci.web.dao.VciFileVolumeDaoI; import com.vci.web.model.VciFileDocClassifyDO; import com.vci.web.model.VciFileObjectDO; import com.vci.web.model.VciFileVolumeDO; import com.vci.web.properties.WebProperties; import com.vci.web.service.VciFileUploadServiceI; import com.vci.web.service.VciFileVolumeServiceI; import com.vci.web.service.WebBoServiceI; import com.vci.web.service.WebSecretServiceI; import com.vci.web.util.PlatformClientUtil; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import static com.vci.file.constant.VciFileLangCodeConstant.*; import static com.vci.frameworkcore.constant.FrameWorkLangCodeConstant.UPLOAD_FAIL; import static com.vci.frameworkcore.constant.FrameWorkLangCodeConstant.UPLOAD_SUCCESS; @Service @VciChangeDocument(btmType = "fileUpload") public class VciFileUploadServiceImpl implements VciFileUploadServiceI { /** * 文件信息数据层 */ @Resource private VciFileObjectDaoI vciFileObjectMapper; /** * 文件类型 */ @Resource private VciFileDocClassifyDaoI fileDocClassifyMapper; /** * 文件仓数据层 */ @Resource private VciFileVolumeDaoI vciFileVolumeMapper; /** * 版本和数据对象工具类 */ @Resource private RevisionModelUtil revisionModelUtil; /** * 平台的客户端 */ @Autowired private PlatformClientUtil platformClientUtil; /** * 配置对象 */ @Autowired private WebProperties webProperties; /** * 密级的服务 */ @Autowired private WebSecretServiceI secretService; /** * 业务对象的服务 */ @Autowired private WebBoServiceI boService; /** * 文件信息共享集合 */ private volatile Map threadFileObjectMap = new ConcurrentHashMap(); /** * 文件的卷服务 */ @Autowired private VciFileVolumeServiceI vciFileVolumeServiceI; /** * 获取文件的扩展名(不带小数点) * @param fileName 文件名称。如XXX.doc * @return */ public String getFileExtension(String fileName){ String fileExtension = ""; if(fileName.indexOf(".") > -1){//处理后缀名 fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1); } return fileExtension; }; /** * 获取文件的名称(不带扩展名) * @param fileName 文件名称。如XXX.doc * @return */ public String getFileNameNoExtension(String fileName){ String fileNameNoExtension = fileName.substring(0,fileName.lastIndexOf(".")); return fileNameNoExtension; } /** * 上传单个文件。 * @param multipartFile 上传的媒体文件 * @param fileObjectDTO 文件的数据传输对象,其中文件支持文件对象或者二进制数组,谨慎使用二进制数组,因为占用内存太大 * @return 文件的显示对象 * @throws VciBaseException 上传文件出错的时候 */ @Override public BaseResult uploadFile(MultipartFile multipartFile, VciFileObjectDTO fileObjectDTO) throws VciBaseException { VciBaseUtil.alertNotNull(fileObjectDTO.getFileDocClassify(), "文档类型编号"); String sourceFileName = multipartFile.getOriginalFilename(); String fileExtension = getFileExtension(sourceFileName); String fileNameNoExtension = getFileNameNoExtension(sourceFileName); VciBaseUtil.alertNotNull(sourceFileName,"文件的名称"); VciFileObjectDO vciFileObjectDO = new VciFileObjectDO(); //校验名称是否有重复 if(fileObjectDTO.isCheckSameFile() && StringUtils.isNotBlank(fileObjectDTO.getOwnbizOid()) ){ if(vciFileObjectMapper.countSameName(fileNameNoExtension,fileExtension,fileObjectDTO.getOwnbizOid(),fileObjectDTO.isUpdateFileFlag(),fileObjectDTO.getOid())>0){ throw new VciBaseException(WebLangCodeConstant.FILE_NAME_REPEAT,new String[0]); } } //如果是有密级的话,需要校验文件的密级 if(webProperties.isSecretRight()){ Integer fileSecret = fileObjectDTO.getSecretGrade(); String fileSecretText = ""; if(fileSecret ==null || fileSecret == -1){ fileSecret = DataSecretEnum.NONE.getValue(); } fileSecretText = DataSecretEnum.getSecretText(fileSecret); if(!secretService.checkDataSecret(fileSecret)){ throw new VciBaseException(WebLangCodeConstant.FILE_UPLOAD_SECRET_ERROR,new String[]{UserSecretEnum.getSecretText(VciBaseUtil.getCurrentUserSecret()) ,fileSecretText}); } String dataSecret = vciFileObjectMapper.selectSecret(fileObjectDTO.getOwnbizOid(),fileObjectDTO.getOwnBtmname()); if(StringUtils.isNotBlank(dataSecret) && VciBaseUtil.getInt(dataSecret) shareFilesToBO(String sourceBtmName, String sourceBusinessOid, String btmName, String businessOid) throws VciBaseException { VciBaseUtil.alertNotNull(sourceBtmName,"原业务类型",sourceBusinessOid,"原业务数据主键",btmName,"业务类型",businessOid,"业务数据主键"); Map conditionMap = new HashMap(); //获取文件仓位置 conditionMap.put("ownbizoid", sourceBusinessOid); conditionMap.put("ownbtmname", sourceBtmName); VciQueryWrapperForDO fileWrapper = new VciQueryWrapperForDO(conditionMap, VciFileObjectDO.class); List vciFileObjectDOList = vciFileObjectMapper.selectByWrapper(fileWrapper); if(vciFileObjectDOList.size()>0){ return shareFile(vciFileObjectDOList, businessOid, btmName); } return BaseResult.successMsg(UPLOAD_SUCCESS); } /** * 将原业务数据下的非表单相关文件共享到其他的业务类型下 * @param sourceBtmName 原业务类型 * @param sourceBusinessOid 原业务数据主键 * @param btmName 业务类型 * @param businessOid 业务数据主键 * @return 共享后的文件显示对象 * @throws VciBaseException 参数为空或者保存出错的时候抛出异常 */ @Override public BaseResult shareAnnexFilesToBO(String sourceBtmName, String sourceBusinessOid, String btmName, String businessOid) throws VciBaseException { VciBaseUtil.alertNotNull(sourceBtmName,"原业务类型",sourceBusinessOid,"原业务数据主键",btmName,"业务类型",businessOid,"业务数据主键"); Map conditionMap = new HashMap(); //获取文件仓位置 conditionMap.put("ownbizoid", sourceBusinessOid); conditionMap.put("ownbtmname", sourceBtmName); List classifyList = new ArrayList<>(); classifyList.add(VciFileDefaultValueConstant.TABLE_FILE_CLASSIFY_DVF); classifyList.add(VciFileDefaultValueConstant.TABLE_FILE_CLASSIFY_PDF); classifyList.add(VciFileDefaultValueConstant.TABLE_FILE_CLASSIFY_XML); VciQueryWrapperForDO fileWrapper = new VciQueryWrapperForDO(conditionMap, VciFileObjectDO.class); fileWrapper.notIn("fileDocClassify", VciBaseUtil.toInSql(VciBaseUtil.list2String(classifyList))); List vciFileObjectDOList = vciFileObjectMapper.selectByWrapper(fileWrapper); if(vciFileObjectDOList.size()>0){ return shareFile(vciFileObjectDOList, businessOid, btmName); } return BaseResult.successMsg(UPLOAD_SUCCESS); } /** * 共享文件 * @param sourceFileObjectList 原文件DO对象集合 * @param businessOid 业务主键 * @param btmName 业务类型 * @return 文件信息显示对象 */ private BaseResult shareFile(List sourceFileObjectList, String businessOid, String btmName) throws VciBaseException { List resultList = new ArrayList(); //创建共享文件的信息 Map sourceDOMap = sharedFileObject(sourceFileObjectList,businessOid,btmName,resultList); //共享文件 sharedFile(resultList, sourceDOMap); List voList = new ArrayList(); for(VciFileObjectDO vciFileObjectDO : resultList){ VciFileObjectVO vciFileObjectVO = new VciFileObjectVO(); BeanUtil.convert(vciFileObjectDO,vciFileObjectVO); voList.add(vciFileObjectVO); } BaseResult baseResult = new BaseResult(); baseResult.setMsg(UPLOAD_SUCCESS); baseResult.setSuccess(true); baseResult.setData(voList); return baseResult; } /** * 共享文件信息 * @param sourceFileObjectList 共享文件信息DO集合 * @param businessOid 业务主键 * @param btmName 业务主键 * @param resultList 共享后文件信息DO集合 * @return 文件信息显示对象集合,key目标文件主键,value目标文件的原文件DO对象 */ private Map sharedFileObject(List sourceFileObjectList, String businessOid, String btmName,List resultList){ Map sourceDOMap = new HashMap(); Date currentDate = new Date(); String currentUserId = VciBaseUtil.getCurrentUserId(); for(VciFileObjectDO sourceDO : sourceFileObjectList){ VciFileObjectDO targetDO = new VciFileObjectDO(); BeanUtil.convert(sourceDO, targetDO); targetDO.setOid(VciBaseUtil.getPk()); targetDO.setCreator(currentUserId); targetDO.setCreateTime(currentDate); targetDO.setTs(currentDate); targetDO.setLastModifier(currentUserId); targetDO.setLastModifyTime(currentDate); targetDO.setOwner(currentUserId); targetDO.setOwnbizOid(businessOid); targetDO.setOwnBtmname(btmName); String randomPath = VciBaseUtil.getPk(); //文件路径 String filePath = randomPath+File.separator+targetDO.getOid(); targetDO.setFilePath(filePath); resultList.add(targetDO); sourceDOMap.put(targetDO.getOid(), sourceDO); } //保存 if(resultList.size()>0){ int batchInsert = vciFileObjectMapper.batchInsert(resultList); if(batchInsert<=0){ throw new VciBaseException(FILE_SHARED_FAIL); } } return sourceDOMap; } /** * 文件共享 * @param resultList 共享文件信息 * @param sourceDOMap 共享文件的原文件集合 */ private void sharedFile(List resultList, Map sourceDOMap){ Set volumeOidSet = new HashSet(); for(VciFileObjectDO targetDO : resultList){ volumeOidSet.add(targetDO.getPkFileVolume()); } Map volumeDOMap = new HashMap<>(); List volumeList = vciFileVolumeMapper.selectByPrimaryKeyCollection(volumeOidSet); for(VciFileVolumeDO volumeDO : volumeList){ volumeDOMap.put(volumeDO.getOid(), volumeDO); } for(VciFileObjectDO targetDO : resultList){ //文仓 VciFileVolumeDO vciFileVolumeDO = volumeDOMap.get(targetDO.getPkFileVolume()); //原文件路径 VciFileObjectDO sourceDO = sourceDOMap.get(targetDO.getOid()); String sourceFilePath = vciFileVolumeDO.getVolumePath()+File.separator+sourceDO.getFilePath(); if(vciFileVolumeDO == null){ throw new VciBaseException(FILE_VOLUME_NOT_EXIST); } //本地协议 if(vciFileVolumeDO.getTransProtocol().equals(VciFileTransProtocolEnum.LOCAL.getValue())){ String targetFilePath = vciFileVolumeDO.getVolumePath()+File.separator+targetDO.getFilePath(); try { File targetFile = new File(targetFilePath); File targetDirFile = targetFile.getParentFile(); if(!targetDirFile.exists()){ targetDirFile.mkdirs(); } if(!targetFile.exists()){ targetFile.createNewFile(); } File sourceFile = new File(sourceFilePath); LocalFileUtil.copyFile(sourceFile, targetFile); }catch (Exception e){ throw new VciBaseException(UPLOAD_FAIL); } } } } /** * 将文件共享到其他的业务类型下 * @param fileOid 文件主键 * @param btmName 业务类型 * @param businessOid 业务数据主键 * @return 文件信息显示对象 * @throws VciBaseException */ @Override public BaseResult shareFilesToBO(String fileOid, String btmName, String businessOid) throws VciBaseException { VciBaseUtil.alertNotNull(fileOid,"文件主键", btmName,"业务类型", businessOid,"业务数据主键"); Map conditionMap = new HashMap(); //获取文件仓位置 conditionMap.put("oid", fileOid); VciQueryWrapperForDO fileWrapper = new VciQueryWrapperForDO(conditionMap, VciFileObjectDO.class); List vciFileObjectDOList = vciFileObjectMapper.selectByWrapper(fileWrapper); if(vciFileObjectDOList.size()>0){ return shareFile(vciFileObjectDOList, businessOid, btmName); } return BaseResult.successMsg(UPLOAD_SUCCESS); } /** * 批量将文件共享到其他的业务类型下 * @param fileOidCollection 文件主键集合 * @param btmName 业务类型 * @param businessOid 业务数据主键 * @return 文件信息显示对象 * @throws VciBaseException 保存数据会抛出异常 */ @Override public BaseResult shareFilesToBO(Collection fileOidCollection, String btmName, String businessOid) throws VciBaseException { VciBaseUtil.alertNotNull(btmName,"业务类型", businessOid,"业务数据主键"); if(fileOidCollection.size()<=0){ throw new VciBaseException(FILE_NOT_EXIST_CANNOT_SHARED); } List vciFileObjectDOS = vciFileObjectMapper.selectByPrimaryKeyCollection(fileOidCollection); if(vciFileObjectDOS.size()>0){ return shareFile(vciFileObjectDOS, businessOid, btmName); } return BaseResult.successMsg(UPLOAD_SUCCESS); } /** * 转换路径中的\。 * @param strFileName 路径 * @return */ private String hanlderOS(String strFileName) { String strNewFileName = strFileName; String osStr = System.getProperty("os.name"); if ( !osStr.startsWith("Win") ) { //Unix strNewFileName = strNewFileName.replace('\\','/'); } return strNewFileName; } /** * 分片上传 * @param multipartFile 片文件 * @param offSet 文件偏移量 * @param fileOid 文件主键 * @return 文件信息显示对象 * @throws VciBaseException 保存数据会抛出异常 */ @Override public BaseResult uploadFileForOtherService(MultipartFile multipartFile, long offSet, String fileOid) throws VciBaseException { BaseResult baseResult = new BaseResult(); //获取文件上传时的初始化信息 VciFileObjectDTO vciFileObjectDTO = null; if(threadFileObjectMap.keySet().contains(fileOid)){ vciFileObjectDTO = threadFileObjectMap.get(fileOid); } if(vciFileObjectDTO == null){ throw new VciBaseException(FILE_INFO_NOT_EXIST); } VciFileObjectDO vciFileObjectDO = vciFileObjectMapper.selectByPrimaryKey(fileOid); if(vciFileObjectDO == null){ throw new VciBaseException(FILE_INFO_NOT_EXIST); } VciFileVolumeDO vciFileVolumeDO = vciFileVolumeMapper.selectByPrimaryKey(vciFileObjectDO.getPkFileVolume()); if(vciFileVolumeDO == null){ throw new VciBaseException(FILE_VOLUME_NOT_EXIST); } //获取存储路径,及文件大小 String targetPath = vciFileVolumeDO.getVolumePath()+File.separator+vciFileObjectDO.getFilePath(); long fileSize = vciFileObjectDO.getFileSize(); //上传文件 targetPath = hanlderOS(targetPath); RandomAccessFile destfile = null; try { File targetFile = new File(targetPath); File targetDir = targetFile.getParentFile(); if(!targetDir.exists()){ targetDir.mkdirs(); } if(!targetFile.exists()){ targetFile.createNewFile(); } destfile = new RandomAccessFile(targetFile, "rw"); destfile.seek(offSet); destfile.write(multipartFile.getBytes(), 0, multipartFile.getBytes().length); //说明文件上传完成 if((fileSize-(offSet+multipartFile.getBytes().length))<=0){ if(vciFileObjectDTO.isEffect()){ vciFileObjectDO.setLcStatus(FileLCStatus.FILE_RELEASE.getValue()); vciFileObjectMapper.updateByPrimaryKey(vciFileObjectDO); } //说明是文件修改,需修改原文件信息、删除新初始化的文件信息及文仓中的文件 if(vciFileObjectDTO.isUpdateFileFlag() && StringUtils.isNotBlank(vciFileObjectDTO.getFileOid())){ VciFileObjectDO sourceFileObject = vciFileObjectMapper.selectByPrimaryKey(vciFileObjectDTO.getFileOid()); String oldFilePath = sourceFileObject.getFilePath(); sourceFileObject.setId(vciFileObjectDO.getId()); sourceFileObject.setName(vciFileObjectDO.getName()); sourceFileObject.setFilePath(vciFileObjectDO.getFilePath()); sourceFileObject.setFileDocClassifyName(vciFileObjectDO.getFileDocClassifyName()); sourceFileObject.setFileDocClassify(vciFileObjectDO.getFileDocClassify()); sourceFileObject.setOwnBtmname(vciFileObjectDO.getOwnBtmname()); sourceFileObject.setOwnbizOid(vciFileObjectDO.getOwnbizOid()); sourceFileObject.setPkFileVolume(vciFileObjectDO.getPkFileVolume()); sourceFileObject.setFileExtension(vciFileObjectDO.getFileExtension()); sourceFileObject.setFileSize(vciFileObjectDO.getFileSize()); VciFileVolumeDO sourceVolume = vciFileVolumeMapper.selectByPrimaryKey(sourceFileObject.getPkFileVolume()); //删除文仓中的文件 String sourceFilePath = sourceVolume.getVolumePath()+File.separator+oldFilePath; File sourceFile = new File(sourceFilePath); if(sourceFile.exists()){ sourceFile.delete(); } File sourceDir = new File(sourceFile.getParentFile().getAbsolutePath()); File[] files = sourceDir.listFiles(); if(sourceDir.exists() && (files == null|| files.length == 0)){ sourceDir.delete(); } //删除文件信息 vciFileObjectMapper.deleteByPrimaryKey(fileOid); vciFileObjectMapper.updateByPrimaryKey(sourceFileObject); } VciFileObjectVO vciFileObjectVO = new VciFileObjectVO(); BeanUtil.convert(vciFileObjectDO,vciFileObjectVO); baseResult.setObj(vciFileObjectVO); threadFileObjectMap.remove(fileOid); } baseResult.setMsg(UPLOAD_SUCCESS); baseResult.setSuccess(true); } catch (Exception e) { throw new VciBaseException(UPLOAD_FAIL); } finally { if(destfile!=null){ try { destfile.close(); } catch (IOException e) { e.printStackTrace(); } } } return baseResult; } /** * 其他服务上传文件之前初始化文件信息 * @param vciFileObjectDTOS 文件信息传输对象 * @return 文件信息显示对象 * @throws VciBaseException 保存数据会抛出异常 */ @Override public BaseResult initUploadFileInfo(Collection vciFileObjectDTOS) throws VciBaseException { for(VciFileObjectDTO vciFileObjectDTO : vciFileObjectDTOS){ VciBaseUtil.alertNotNull(vciFileObjectDTO,"文件信息传输对象", vciFileObjectDTO.getOwnbizOid(),"业务数据主键", vciFileObjectDTO.getOwnBtmname(),"业务数据业务类型", vciFileObjectDTO.getId(),"文件显示名称", vciFileObjectDTO.getName(),"文件实际名称", vciFileObjectDTO.getFileSize(),"文件大小", vciFileObjectDTO.getFileExtension(),"文件扩展名"); } List vciFileVolumeDOList = vciFileVolumeMapper.selectAll(); if(vciFileVolumeDOList.size()<=0){ throw new VciBaseException(VciFileLangCodeConstant.FILE_VOLUME_NOT_EXIST); } VciFileVolumeDO vciFileVolumeDO = vciFileVolumeDOList.get(0); List vciFileObjectDOS = new ArrayList<>(); List vciFileObjectVOS = new ArrayList(); for(VciFileObjectDTO vciFileObjectDTO : vciFileObjectDTOS){ VciFileObjectDO vciFileObjectDO = new VciFileObjectDO(); BeanUtil.convert(vciFileObjectDTO, vciFileObjectDO); vciFileObjectDO.setPkFileVolume(vciFileVolumeDO.getOid()); if(StringUtils.isBlank(vciFileObjectDO.getId())){ vciFileObjectDO.setName(vciFileObjectDO.getId()); } if(vciFileObjectDO.getSecretGrade() == null || vciFileObjectDO.getSecretGrade() == 0){ vciFileObjectDO.setSecretGrade(DataSecretEnum.NONE.getValue()); } revisionModelUtil.wrapperForAdd(vciFileObjectDO); String targetFilePath = VciBaseUtil.getPk()+File.separator+vciFileObjectDO.getOid(); vciFileObjectDO.setFilePath(targetFilePath); vciFileObjectDO.setLcStatus(FileLCStatus.TRANSING.getValue()); vciFileObjectDOS.add(vciFileObjectDO); VciFileObjectVO vciFileObjectVO = new VciFileObjectVO(); BeanUtil.convert(vciFileObjectDO, vciFileObjectVO); vciFileObjectVOS.add(vciFileObjectVO); threadFileObjectMap.put(vciFileObjectDO.getOid(),vciFileObjectDTO); } if(vciFileObjectDOS.size()>0){ int insert = vciFileObjectMapper.batchInsert(vciFileObjectDOS); if(insert<=0){ throw new VciBaseException(FILE_INFO_INIT_FAIL); } } BaseResult baseResult = new BaseResult(); baseResult.setData(vciFileObjectVOS); baseResult.setMsg(FILE_INFO_INIT_SUCCESS); baseResult.setSuccess(true); return baseResult; } }