package com.deloitte.system.service; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSONArray; import com.common.aws.util.S3Util; import com.common.core.constant.GlobalConst; import com.common.core.enums.ResultCodeEnum; import com.common.core.exception.BizException; import com.common.core.utils.UUID; import com.common.core.utils.*; import com.common.redis.util.RedisUtil; import com.deloitte.system.request.FileRequest; import com.j256.simplemagic.ContentInfoUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import software.amazon.awssdk.core.ResponseBytes; import software.amazon.awssdk.services.s3.model.GetObjectResponse; import software.amazon.awssdk.services.s3.model.S3Exception; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.bind.DatatypeConverter; import java.io.*; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.*; import static com.common.core.constant.GlobalConst.PIPL_UNCONFIRM_FILE; @Slf4j @Service public class FileService { @Autowired private RedisUtil redisUtil; @Value("${salesforce.referer}") private String refererUrl; @Value("${aws.s3.bucketName}") private String bucketName; public String upload(String txId,FileRequest fileRequest){ log.info("上传文件txId:"+txId); String key=uploadFile(fileRequest); String keyName = PIPL_UNCONFIRM_FILE + txId; redisUtil.set(keyName, new JSONArray(Arrays.asList(key)), GlobalConst.DATA_EXPIRE); return key; } public List batchUpload(String txId, List fileList) { log.info("上传文件txId:"+txId); List keys=new ArrayList<>(); try { fileList.forEach(fileRequest -> { String key = uploadFile(fileRequest); keys.add(key); }); String keyName = PIPL_UNCONFIRM_FILE + txId; redisUtil.set(keyName, new JSONArray((List)keys), GlobalConst.DATA_EXPIRE); }catch (Exception e){ this.cleanS3File(keys); throw e; } return keys; } public String uploadFile(FileRequest fileRequest){ if (fileRequest == null) { throw new BizException(ResultCodeEnum.NO_DATA); } log.info("fileName:{}", fileRequest.getFileName()); //文件大小限制,最大单文件30M if(fileRequest.getSize()>31457280){ throw new BizException(ResultCodeEnum.FILE_SIZE_EXCEED); } if(fileRequest.getFile().startsWith("base64,")){ String contentType= ContentInfoUtil.findExtensionMatch(fileRequest.getFileName())==null? "text/plain": ContentInfoUtil.findExtensionMatch(fileRequest.getFileName()).getMimeType(); fileRequest.setFile("data:"+contentType+";"+fileRequest.getFile()); } SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); String oldFileName = fileRequest.getFileName(); String suffix=oldFileName.lastIndexOf(".")==-1?"":oldFileName.substring(oldFileName.lastIndexOf(".")); String newFileName = UUID.randomUUID().toString().replace("-","")+suffix; String key = sdf.format(new Date()) + "/" + newFileName; // 将文件base64存入缓存 MultipartFile multipartFile = null; try { multipartFile = BASE64DecodedMultipartFile.base64ToMultipart(fileRequest.getFile()); } catch (IOException e) { log.error("上传文件失败:",e); } if (multipartFile == null) { throw new BizException("未找到文件"); } String bucketName = SpringContextUtils.getEnvParam("aws.s3.bucketName"); S3Util.uploadFile(multipartFile, key,bucketName); return key; } public String uploadFileNoSize(FileRequest fileRequest){ if (fileRequest == null) { throw new BizException(ResultCodeEnum.NO_DATA); } log.info("fileName:{}", fileRequest.getFileName()); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd"); String oldFileName = fileRequest.getFileName(); String suffix=oldFileName.lastIndexOf(".")==-1?"":oldFileName.substring(oldFileName.lastIndexOf(".")); String newFileName = UUID.randomUUID().toString().replace("-","")+suffix; String key = sdf.format(new Date()) + "/" + newFileName; // 将文件base64存入缓存 MultipartFile multipartFile = null; try { multipartFile = BASE64DecodedMultipartFile.base64ToMultipart(fileRequest.getFile()); } catch (IOException e) { log.error("上传文件失败:",e); } if (multipartFile == null) { throw new BizException("未找到文件"); } String bucketName = SpringContextUtils.getEnvParam("aws.s3.bucketName"); S3Util.uploadFile(multipartFile, key,bucketName); return key; } public void cleanS3File(Collection keyList){ if (!keyList.isEmpty()){ String bucketName = SpringContextUtils.getEnvParam("aws.s3.bucketName"); S3Util.deleteByKeys(new ArrayList(keyList),bucketName); } } private boolean checkReferer(String referer){ String[] refererUrls=refererUrl.split(","); for (String str : refererUrls) { if(str.equalsIgnoreCase(referer)||str.substring(0,str.length()-1).equalsIgnoreCase(referer)){ return true; } } return false; } public void preview(HttpServletRequest request,HttpServletResponse response,String key){ String referer = request.getHeader("Referer"); if (StrUtil.isBlank(referer)) { throw new BizException(ResultCodeEnum.NO_PERMISSION); } //referer 截取 String replace = referer.replace("https://", ""); String newReferer = "https://"+replace.substring(0, replace.indexOf("/")+1); if (!checkReferer(newReferer)) { throw new BizException(ResultCodeEnum.NO_PERMISSION); } String fileName = key.substring(key.lastIndexOf("/")+1); log.info("文件预览key:{},fileName:{}",key,fileName); OutputStream out = null; try { response.setCharacterEncoding("utf-8"); String s = ContentInfoUtil.findExtensionMatch(fileName).getMimeType(); log.info("文件预览key:{},contentType:{}",key,s); if (StrUtil.isNotBlank(s)) { response.setContentType(s); } else{ response.setContentType(MediaType.ALL_VALUE); } String agent = request.getHeader("User-Agent"); if(agent.toLowerCase().contains("firefox")) { response.setHeader("Content-Disposition", "inline; filename==?UTF-8?B?" + new String(Base64.encodeBase64(fileName.getBytes("UTF-8")))+"?="); }else { response.setHeader("Content-Disposition", "inline; filename=" + new String(fileName.getBytes("gbk"),"iso8859-1")); } ResponseBytes objectBytes = S3Util.downloadFile(key,bucketName); InputStream inputStream = objectBytes.asInputStream(); BufferedInputStream bufInputStream = new BufferedInputStream(inputStream); out = response.getOutputStream(); byte[] buf = new byte[1024]; int len; while ((len = bufInputStream.read(buf)) > 0) { out.write(buf, 0, len); } response.flushBuffer(); out.close(); } catch (IOException e) { log.error("文件预览失败:",e); throw new BizException(ResultCodeEnum.RT_ERROR); }catch (S3Exception e) { log.error("文件预览失败:",e); throw new BizException(e.getMessage()); } } public void download(HttpServletRequest request,HttpServletResponse response,String key){ String referer = request.getHeader("Referer"); log.info("referer:"+referer); if (StrUtil.isBlank(referer)) { throw new BizException(ResultCodeEnum.NO_PERMISSION); } //referer 截取 String replace = referer.replace("https://", ""); String newReferer = "https://"+replace.substring(0, replace.indexOf("/")+1); if (!checkReferer(newReferer)) { throw new BizException(ResultCodeEnum.NO_PERMISSION); } OutputStream out = null; try { String fileName = request.getParameter("fileName"); if (StrUtil.isBlank(fileName)) { throw new BizException("fileName不能为空"); } log.info("文件下载key:{},fileName:{}",key,fileName); response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "No-cache"); response.setDateHeader("Expires", 0); response.setContentType("application/octet-stream"); String agent = request.getHeader("User-Agent"); if (null != agent && agent.contains("MSIE") || null != agent && agent.contains("Trident") || null != agent && agent.contains("Edge")) { // ie浏览器及Edge浏览器 fileName = java.net.URLEncoder.encode(fileName, "UTF-8"); } else if (null != agent && agent.contains("Mozilla")) { // 火狐,Chrome等浏览器 fileName = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1); }else { // 其它浏览器 fileName = URLEncoder.encode(fileName, "utf-8"); } response.setHeader("Content-Disposition", "attachment; filename="+fileName); ResponseBytes objectBytes = S3Util.downloadFile(key,bucketName); InputStream inputStream = objectBytes.asInputStream(); BufferedInputStream bufInputStream = new BufferedInputStream(inputStream); out = response.getOutputStream(); byte[] buf = new byte[1024]; int len; while ((len = bufInputStream.read(buf)) > 0) { out.write(buf, 0, len); } response.flushBuffer(); out.close(); } catch (IOException e) { log.error("文件下载失败:",e); throw new BizException(ResultCodeEnum.RT_ERROR); }catch (S3Exception e) { log.error("文件下载失败:",e); throw new BizException(e.getMessage()); } } public void deleteFile(List keys){ if (CollUtil.isEmpty(keys)) { throw new BizException(ResultCodeEnum.NO_DATA); } String bucketName = SpringContextUtils.getEnvParam("aws.s3.bucketName"); S3Util.deleteByKeys(keys,bucketName); } public void convert(HttpServletRequest request,HttpServletResponse response) { //从param参数中获取fileName,如果为空则取当前时间为文件名 String fileName = request.getParameter("fileName"); fileName = fileName==null? DateUtils.format(new Date()) : fileName; response.setContentType("application/pdf"); //要保存的文件名 response.setHeader("Content-Disposition", "inline;filename=" + fileName + ".pdf"); response.addHeader("Pargam", "no-cache"); response.addHeader("Cache-Control", "no-cache"); ServletOutputStream outputStream = null; BufferedReader bufferReader=null; String base64Str=""; try { if(StringUtils.isNotBlank(request.getParameter("base64Str"))){ base64Str=request.getParameter("base64Str"); }else { bufferReader = new BufferedReader(request.getReader()); StringBuilder sb = new StringBuilder(); String line = null; while ((line = bufferReader.readLine()) != null) { sb.append(line); } String body = sb.toString(); String[] strings = body.split("&"); Map valueMap = new HashMap<>(); for (String string : strings) { String[] keyValue = string.split("=", 2); String key = keyValue[0]; String value = keyValue[1]; valueMap.put(URLDecoder.decode(key, "UTF-8"), URLDecoder.decode(value, "UTF-8")); } base64Str = valueMap.get("base64Str"); } outputStream = response.getOutputStream(); byte[] b = DatatypeConverter.parseBase64Binary(base64Str); outputStream.write(b, 0, b.length); } catch (IOException e) { log.error("文件转换异常:",e); throw new BizException(ResultCodeEnum.RT_ERROR); }finally { try { if(bufferReader!=null)bufferReader.close(); } catch (IOException e) { } } } public static String getFilecharset(InputStream inputStream) { //默认GBK String charset = "GBK"; byte[] first3Bytes = new byte[3]; try(BufferedInputStream bis = new BufferedInputStream(inputStream)) { bis.mark(0); int read = bis.read(first3Bytes, 0, 3); // 文件编码为 ANSI if (read == -1) { return charset; } // 文件编码为 Unicode if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) { return "UTF-16LE"; } // 文件编码为 Unicode big endian if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) { return "UTF-16BE"; } // 文件编码为 UTF-8 if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB && first3Bytes[2] == (byte) 0xBF) { return "UTF-8"; } bis.reset(); int loc = 0; while ((read = bis.read()) != -1) { loc++; if (read >= 0xF0) { break; } // 单独出现BF以下的,也算是GBK if (0x80 <= read && read <= 0xBF) { break; } if (0xC0 <= read && read <= 0xDF) { read = bis.read(); // 双字节 (0xC0 - 0xDF) if (0x80 <= read && read <= 0xBF) { // (0x80 // - 0xBF),也可能在GB编码内 continue; } break; } // 也有可能出错,但是几率较小 if (0xE0 <= read && read <= 0xEF) { read = bis.read(); if (0x80 <= read && read <= 0xBF) { read = bis.read(); if (0x80 <= read && read <= 0xBF) { charset = "UTF-8"; } } break; } } } catch (Exception e) { e.printStackTrace(); } return charset; } }