| | |
| | | package com.gkhy.exam.system.service.impl; |
| | | |
| | | import cn.hutool.core.date.DateUtil; |
| | | import cn.hutool.core.io.FileUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.gkhy.exam.common.config.FilePathConfig; |
| | | import com.gkhy.exam.common.enums.ResourceTypeEnum; |
| | | import com.gkhy.exam.common.exception.ApiException; |
| | | import com.gkhy.exam.common.utils.M3u8Utils; |
| | | import com.gkhy.exam.common.utils.MinioUtils; |
| | | import com.gkhy.exam.common.utils.*; |
| | | import com.gkhy.exam.system.domain.ExResource; |
| | | import com.gkhy.exam.system.domain.vo.UploadObjectVO; |
| | | import com.gkhy.exam.system.mapper.ExResourceMapper; |
| | | import com.gkhy.exam.system.service.SysCommonService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.io.FileUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
| | |
| | | import sun.misc.BASE64Decoder; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.io.*; |
| | | import java.time.LocalDateTime; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Paths; |
| | | import java.util.*; |
| | | import java.util.concurrent.CountDownLatch; |
| | | |
| | |
| | | private M3u8Utils m3u8Utils; |
| | | @Autowired |
| | | private MinioUtils minioUtils; |
| | | @Autowired |
| | | private ExResourceMapper resourceMapper; |
| | | @Resource |
| | | private FilePathConfig filePath; |
| | | private FilePathConfig filePathConfig; |
| | | |
| | | // 使用HashSet存储允许的文件格式,提高查找效率 |
| | | private final HashSet<String> FORMATSET = new HashSet<>(Arrays.asList(".mp4", ".mp3", ".xls", ".xlsx", ".doc", ".docx", ".ppt", ".pptx", ".pdf")); |
| | | |
| | | |
| | | @Resource(name = "threadPoolTaskExecutor") |
| | | private ThreadPoolTaskExecutor poolTaskExecutor; |
| | | |
| | | String projectUrl = System.getProperty("user.dir").replaceAll("\\\\", "/"); |
| | | |
| | | @Override |
| | | public UploadObjectVO uploadFile(MultipartFile file) { |
| | |
| | | @Override |
| | | public String uploadVideo2M3u8(MultipartFile file) throws Exception { |
| | | String path=m3u8Utils.mediaFileToM3u8(file); |
| | | return upload2M3u8(path); |
| | | String name= upload2M3u8(path,null); |
| | | if(StringUtils.isBlank(name)){ |
| | | throw new ApiException("文件上传失败"); |
| | | } |
| | | return name; |
| | | } |
| | | |
| | | public String localVideo2M3u8(String path,String mainName) throws Exception { |
| | | String paths=m3u8Utils.localFileToM3u8(path,mainName); |
| | | return upload2M3u8(paths,mainName); |
| | | } |
| | | |
| | | |
| | |
| | | * @return 路径 |
| | | * @throws Exception |
| | | */ |
| | | public String upload2M3u8(String path) throws Exception { |
| | | public synchronized String upload2M3u8(String path,String filePrex) throws Exception { |
| | | //存储转码后文件 |
| | | String realPath = path.substring(0, path.lastIndexOf("/")); |
| | | log.info("视频解析后的 realPath {}", realPath); |
| | |
| | | if (null == files || files.length == 0) { |
| | | return null; |
| | | } |
| | | String patch = DateUtil.format(LocalDateTime.now(), "yyyy/MM/") + name.substring(0, name.lastIndexOf(".")) + "/"; |
| | | List<File> errorFile = new ArrayList<>(); |
| | | |
| | | long start = System.currentTimeMillis(); |
| | | // //替换m3u8文件中的路径 |
| | | //替换m3u8文件中的路径 |
| | | |
| | | // FileUtil.replaceTextContent(path, name.substring(0, name.lastIndexOf(".")), |
| | | // aliOssProperties.getMyHostUrl() + filePath.getProxy() + patch + |
| | | // name.substring(0, name.lastIndexOf("."))); |
| | |
| | | CountDownLatch countDownLatch = new CountDownLatch(files.length); |
| | | Arrays.stream(files).forEach(li -> poolTaskExecutor.execute(() -> { |
| | | try (FileInputStream fileInputStream = new FileInputStream(li)) { |
| | | minioUtils.fileUploader(patch + li.getName(), fileInputStream); |
| | | |
| | | String objectKey=li.getName(); |
| | | if(StringUtils.isNotBlank(filePrex)){ |
| | | objectKey=filePrex+"/"+li.getName(); |
| | | } |
| | | minioUtils.fileUploader(objectKey, fileInputStream); |
| | | log.info("文件:{} 正在上传", li.getName()); |
| | | } catch (Exception e) { |
| | | errorFile.add(li); |
| | | log.error("文件:{}上传失败",li.getName()); |
| | | e.printStackTrace(); |
| | | } finally { |
| | | countDownLatch.countDown(); |
| | |
| | | countDownLatch.await(); |
| | | long end = System.currentTimeMillis(); |
| | | log.info("解析文件上传成功,共计:{} 个文件,失败:{},共耗时: {}ms", files.length, errorFile.size(), end - start); |
| | | // try { |
| | | // minioComponent.mkBucket("m3u8"); |
| | | // } catch (Exception e) { |
| | | // log.error("创建Bucket失败!"); |
| | | // } |
| | | |
| | | //异步移除所有文件 |
| | | poolTaskExecutor.execute(() -> { |
| | | deleteFile(projectUrl+filePath.getTempPath()); |
| | | FileUtil.del(realPath); |
| | | log.warn("临时目录 {}已删除", realPath); |
| | | }); |
| | | if (CollectionUtils.isEmpty(errorFile)) { |
| | | return filePath.getProxy() + patch + name; |
| | | return StringUtils.isNotBlank(filePrex)? filePrex+"/"+name:name; |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | public static void deleteFile(String path){ |
| | | File dest = new File(path); |
| | | if (dest.isFile() && dest.exists()) { |
| | | dest.delete(); |
| | | |
| | | @Override |
| | | public boolean uploadSlice( String fileMd5, String chunkName, MultipartFile file) { |
| | | String systemDir=System.getProperty("user.dir"); |
| | | String filePath=systemDir+filePathConfig.getTempPath()+"chunk/"+fileMd5; |
| | | File dirFile=new File(filePath); |
| | | if(!dirFile.exists()){ |
| | | dirFile.mkdirs(); |
| | | } |
| | | filePath=filePath+File.separator+chunkName; |
| | | try { |
| | | file.transferTo(new File(filePath)); |
| | | } catch (FileNotFoundException e) { |
| | | throw new ApiException("找不到文件"); |
| | | } catch (IOException e) { |
| | | throw new ApiException("发生错误,请联系管理员"); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public UploadObjectVO uploadMerge(String fileMd5, String fileName) throws Exception { |
| | | String subfix=fileName.substring(fileName.lastIndexOf("."),fileName.length()); |
| | | checkFileFormat(subfix); |
| | | UploadObjectVO uploadObjectVO=mergeFile(fileMd5,fileName); |
| | | String localPath=uploadObjectVO.getPath(); |
| | | String minioPath=""; |
| | | if(".mp4".equalsIgnoreCase(subfix)){ |
| | | String m3u8Path=m3u8Utils.localFileToM3u8(localPath, fileMd5); |
| | | //计算mp4时长 |
| | | uploadObjectVO.setResourceLength(VideoUtils.getMp4Duration(m3u8Path)); |
| | | uploadObjectVO.setResourceType(ResourceTypeEnum.VIDEO.getCode()); |
| | | minioPath= upload2M3u8(m3u8Path,fileMd5); |
| | | }else{ |
| | | if(".mp3".equalsIgnoreCase(subfix)){ |
| | | uploadObjectVO.setResourceType(ResourceTypeEnum.AUDIO.getCode()); |
| | | //计算mp3时长 |
| | | uploadObjectVO.setResourceLength(VideoUtils.getMp3Duration(localPath)); |
| | | }else{ |
| | | uploadObjectVO.setResourceType(ResourceTypeEnum.DOC.getCode()); |
| | | //计算文件页数 |
| | | uploadObjectVO.setDocPage(DocUtils.getDocPageCount(fileName,Files.newInputStream(Paths.get(localPath)))); |
| | | } |
| | | minioPath=minioUtils.fileUploader(uploadObjectVO.getFilename(), Files.newInputStream(Paths.get(localPath))); |
| | | } |
| | | if(StringUtils.isBlank(minioPath)){ |
| | | throw new ApiException("上传文件失败"); |
| | | } |
| | | //移除文件 |
| | | poolTaskExecutor.execute(() -> { |
| | | FileUtil.del(new File(localPath).getParent()); |
| | | log.warn("临时目录 {}已删除", new File(localPath).getParent()); |
| | | }); |
| | | uploadObjectVO.setPath(minioPath); |
| | | return uploadObjectVO; |
| | | } |
| | | |
| | | private void checkFileFormat(String subfix){ |
| | | if(!FORMATSET.contains(subfix)){ |
| | | throw new ApiException(String.format("文件格式<%s>暂时不支持",subfix)); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public boolean uploadSlice(HttpServletRequest request, String guid, Integer chunk, MultipartFile file) { |
| | | return false; |
| | | public void removeMinioFile(Long resourceId,String path) { |
| | | String md5=""; |
| | | if(path.indexOf("/")!=-1){ |
| | | md5=path.substring(0,path.indexOf("/")); |
| | | }else{ |
| | | md5=path.substring(0,path.lastIndexOf(".")); |
| | | } |
| | | LambdaQueryWrapper<ExResource> lambdaQueryWrapper = Wrappers.<ExResource>lambdaQuery(). |
| | | eq(true, ExResource::getMd5, md5); |
| | | if(resourceId!=null){ |
| | | lambdaQueryWrapper.ne(ExResource::getId,resourceId); |
| | | } |
| | | lambdaQueryWrapper.last(" limit 1"); |
| | | ExResource resource=resourceMapper.selectOne(lambdaQueryWrapper); |
| | | if(resource==null){//不存在共用md5文件 |
| | | //删除minio文件 |
| | | if(path.endsWith(".m3u8")){ |
| | | minioUtils.removeFolder(md5); |
| | | }else{ |
| | | minioUtils.removeFile(path); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public String uploadVideoMerge(String guid, String fileName) { |
| | | return null; |
| | | } |
| | | public UploadObjectVO mergeFile(String fileMd5,String fileName){ |
| | | String subfix = fileName.substring(fileName.lastIndexOf(".")); |
| | | String systemDir=System.getProperty("user.dir"); |
| | | String filePath=systemDir+filePathConfig.getTempPath()+"chunk/"+fileMd5; |
| | | |
| | | @Override |
| | | public String uploadMerge(String guid, String fileName) { |
| | | return null; |
| | | File parentFileDir=new File(filePath); |
| | | if(!parentFileDir.exists()||!parentFileDir.isDirectory()){ |
| | | throw new ApiException("切片文件合并失败,文件不存在"); |
| | | } |
| | | String destPath=systemDir+filePathConfig.getTempPath()+fileMd5; |
| | | File destTempFile=new File(destPath,fileMd5+subfix); |
| | | if(!destTempFile.exists()){ |
| | | destTempFile.getParentFile().mkdirs(); |
| | | } |
| | | try{ |
| | | //destTempFile.createNewFile(); |
| | | for(int i=0;i<Objects.requireNonNull(parentFileDir.listFiles()).length;i++){ |
| | | File partFile=new File(parentFileDir,fileMd5+"-"+i); |
| | | FileOutputStream destTempfos = new FileOutputStream(destTempFile, true); |
| | | //遍历"所有分片文件"到"最终文件"中 |
| | | FileUtils.copyFile(partFile, destTempfos); |
| | | destTempfos.close(); |
| | | } |
| | | FileUtil.del(parentFileDir); |
| | | }catch (Exception e){ |
| | | log.error("切片文件合并,失败原因e:{}", e.getMessage()); |
| | | throw new ApiException("切片文件合并失败:"+e.getMessage()); |
| | | } |
| | | UploadObjectVO uploadObjectVO=new UploadObjectVO(); |
| | | uploadObjectVO.setSize(destTempFile.length()); |
| | | uploadObjectVO.setPath(destTempFile.getPath()); |
| | | uploadObjectVO.setOriginName(fileName); |
| | | uploadObjectVO.setFilename(fileMd5+subfix); |
| | | uploadObjectVO.setMd5(fileMd5); |
| | | return uploadObjectVO; |
| | | } |
| | | |
| | | @Override |