package com.lxzn.framework.utils.file;
|
|
import com.lxzn.framework.domain.filesystem.response.FileUploadResult;
|
import com.lxzn.framework.domain.nc.response.DocumentCode;
|
import com.lxzn.framework.exception.ExceptionCast;
|
import com.lxzn.framework.utils.SHA256Util;
|
import com.lxzn.framework.utils.date.DateUtil;
|
import lombok.SneakyThrows;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.lang3.StringUtils;
|
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.stereotype.Component;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import javax.servlet.http.HttpServletResponse;
|
import java.io.*;
|
import java.net.URLEncoder;
|
import java.nio.channels.FileChannel;
|
import java.nio.file.Files;
|
import java.nio.file.Paths;
|
import java.security.MessageDigest;
|
import java.util.ArrayList;
|
import java.util.Date;
|
import java.util.List;
|
|
@Slf4j
|
@Component
|
public class FileUtil {
|
|
private static String fileUploadFolder;
|
|
@Value("${fileHomePath}")
|
public void setFileUploadFolder(String fileUploadFolder) {
|
FileUtil.fileUploadFolder = fileUploadFolder;
|
}
|
|
public static FileUploadResult uploadFile(MultipartFile file) {
|
if(file == null || file.isEmpty())
|
return null;
|
String fileName = file.getOriginalFilename();
|
String suffix = getFileSuffix(fileName);
|
// if(StringUtils.isBlank(suffix))
|
// return null;
|
Date currentDate = DateUtil.getNow();
|
String monthStr = DateUtil.format(currentDate, DateUtil.STR_YEAR_MONTH);
|
//相对路径
|
String relativePath = "/" + monthStr + "/" + DateUtil.getDayStr(currentDate) + "/";
|
//绝对路径
|
String absolutePath = fileUploadFolder + "/" + monthStr + "/" + DateUtil.getDayStr(currentDate) + "/";
|
String fileNameNonSuffix = getFilenameNonSuffix(fileName);
|
if(fileNameNonSuffix == null)
|
return null;
|
String encodeFileName = SHA256Util.getSHA256Str(fileNameNonSuffix + System.currentTimeMillis());
|
Long fileSize = file.getSize();
|
boolean b = uploadFile(file, absolutePath, encodeFileName);
|
if(!b)
|
return null;
|
FileUploadResult dto = new FileUploadResult();
|
dto.setFileName(fileNameNonSuffix);
|
dto.setEncodeFileName(encodeFileName);
|
dto.setFilePath(relativePath);
|
dto.setFileSize(fileSize);
|
dto.setFileSuffix(suffix);
|
return dto;
|
}
|
|
public static FileUploadResult uploadFile(String fileName, InputStream fis) {
|
if(fis == null)
|
return null;
|
//String fileName = file.getName();
|
String suffix = getFileSuffix(fileName);
|
// if(StringUtils.isBlank(suffix))
|
// return null;
|
Date currentDate = DateUtil.getNow();
|
String monthStr = DateUtil.format(currentDate, DateUtil.STR_YEAR_MONTH);
|
//相对路径
|
String relativePath = "/" + monthStr + "/" + DateUtil.getDayStr(currentDate) + "/";
|
//绝对路径
|
String absolutePath = fileUploadFolder + "/" + monthStr + "/" + DateUtil.getDayStr(currentDate) + "/";
|
String fileNameNonSuffix = getFilenameNonSuffix(fileName);
|
if(fileNameNonSuffix == null) {
|
return null;
|
}
|
|
String encodeFileName = SHA256Util.getSHA256Str(fileNameNonSuffix + System.currentTimeMillis());
|
//Long fileSize = fis;
|
long b = uploadFile(fis, absolutePath, encodeFileName);
|
if(b <= 0)
|
return null;
|
FileUploadResult dto = new FileUploadResult();
|
dto.setFileName(fileNameNonSuffix);
|
dto.setEncodeFileName(encodeFileName);
|
dto.setFilePath(relativePath);
|
dto.setFileSize(b);
|
dto.setFileSuffix(suffix);
|
return dto;
|
}
|
|
public static void downLoadFile(HttpServletResponse response, String fileName, String filePath, String toFileName) {
|
String absolutePath = fileUploadFolder + filePath;
|
File file = new File(absolutePath , fileName);
|
if(file.exists()) {
|
byte[] buffer = new byte[1024];
|
FileInputStream fis = null;
|
BufferedInputStream bis = null;
|
try {
|
response.setHeader("Content-Type", "application/octet-stream;charset=utf-8"); // 告诉浏览器输出内容为流
|
//response.setHeader("Content-Disposition", "attachment;fileName="+ new String(toFileName.getBytes("UTF-8"),"ISO-8859-1"));
|
response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(toFileName, "UTF-8"));
|
fis = new FileInputStream(file);
|
bis = new BufferedInputStream(fis);
|
OutputStream os = response.getOutputStream();
|
int i = bis.read(buffer);
|
while (i != -1) {
|
os.write(buffer, 0, i);
|
i = bis.read(buffer);
|
}
|
}catch (Exception e) {
|
log.error(e.getMessage(), e.getStackTrace());
|
}finally {
|
if(bis != null) {
|
try {
|
bis.close();
|
} catch (IOException e) {
|
}
|
}
|
if(fis != null) {
|
try {
|
fis.close();
|
} catch (IOException e) {
|
}
|
}
|
}
|
}else {
|
ExceptionCast.cast(DocumentCode.DOC_PUBLISH_FILE_NOT_EXIST);
|
}
|
}
|
/**
|
* 上传文件工具类
|
* @param multipartFile
|
* @param path
|
* @param fileNewName 新的文件名
|
* @return
|
*/
|
public static boolean uploadFile(MultipartFile multipartFile, String path, String fileNewName) {
|
File targetFile = new File(path, fileNewName);
|
if(!targetFile.getParentFile().exists()){
|
targetFile.getParentFile().mkdirs();
|
}
|
try {
|
multipartFile.transferTo(targetFile);
|
return true;
|
} catch (Exception e) {
|
log.error(e.getMessage(), e.getStackTrace());
|
return false;
|
}
|
}
|
|
public static long uploadFile(InputStream fis, String path, String fileNewName) {
|
File targetFile = new File(path, fileNewName);
|
if(!targetFile.getParentFile().exists()){
|
targetFile.getParentFile().mkdirs();
|
}
|
try {
|
FileOutputStream fos = new FileOutputStream(targetFile);
|
//FileInputStream fis = new FileInputStream(file);
|
byte[] bytes = new byte[1024 * 4];
|
int index = 0;
|
long total = 0;
|
while ((index = fis.read(bytes)) != -1) {
|
fos.write(bytes, 0, index);
|
total =+ index;
|
}
|
fis.close();
|
fos.flush();
|
fos.close();
|
return total;
|
} catch (Exception e) {
|
log.error(e.getMessage());
|
}
|
return -1;
|
}
|
|
/**
|
* 获取文件后缀 无后缀文件返回NULL
|
* @param fileName
|
* @return
|
*/
|
public static String getFileSuffix(String fileName) {
|
if (fileName.contains(".")) {
|
String suffix = fileName.substring(fileName.lastIndexOf('.') + 1);
|
return suffix;
|
}else {
|
return null;
|
}
|
|
}
|
|
/**
|
* 获取文件名 不带后缀和点号
|
* @param fileName
|
* @return
|
*/
|
public static String getFilenameNonSuffix(String fileName) {
|
if (fileName.contains(".")) {
|
String filename = fileName.substring(0, fileName.lastIndexOf('.'));
|
return filename;
|
}else {
|
return fileName;
|
}
|
|
}
|
|
public static List<String> readFile(String fileEncodeName, String filePath) {
|
String absolutePath = fileUploadFolder + filePath;
|
File file = new File(absolutePath , fileEncodeName);
|
if(!file.exists() || file.isDirectory())
|
ExceptionCast.cast(DocumentCode.DOC_PUBLISH_FILE_NOT_EXIST);
|
String charset = checkFileEncode(file);
|
if(charset == null)
|
ExceptionCast.cast(DocumentCode.DOC_PUBLISH_FILE_NOT_EXIST);
|
List<String> readList = new ArrayList<>();
|
String tempString = null;
|
BufferedReader reader = null;
|
try {
|
reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
|
while ((tempString = reader.readLine()) != null) {
|
readList.add(tempString);
|
}
|
if(readList.isEmpty())
|
return null;
|
return readList;
|
} catch (Exception e) {
|
log.error(e.getMessage(), e.getStackTrace());
|
return null;
|
} finally {
|
if(reader != null) {
|
try {
|
reader.close();
|
} catch (IOException e) {
|
log.error(e.getMessage(), e.getStackTrace());
|
}
|
}
|
}
|
}
|
|
private static String checkFileEncode(File file) {
|
String charset = "GBK";
|
byte[] first3Bytes = new byte[3];
|
BufferedInputStream bis = null;
|
try {
|
boolean checked = false;
|
bis = new BufferedInputStream(new FileInputStream(file));
|
bis.mark(0);
|
int read = bis.read(first3Bytes, 0, 3);
|
if (read == -1)
|
return charset;
|
if(first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE ) {
|
charset = "UTF-16LE";
|
checked = true;
|
}else if ( first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF ) {
|
charset = "UTF-16BE";
|
checked = true;
|
}else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB && first3Bytes[2] == (byte) 0xBF ) {
|
charset = "UTF-8";
|
checked = true;
|
}
|
bis.reset();
|
if (!checked) {
|
while ((read = bis.read()) != -1 ) {
|
if ( read >= 0xF0 )
|
break;
|
if ( 0x80 <= read && read <= 0xBF ) // 单独出现BF以下的,也算是GBK
|
break;
|
if ( 0xC0 <= read && read <= 0xDF ) {
|
read = bis.read();
|
if ( 0x80 <= read && read <= 0xBF ) // 双字节 (0xC0 - 0xDF) (0x80 - 0xBF),也可能在GBK编码内
|
continue;
|
else
|
break;
|
} else 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;
|
} else
|
break;
|
} else {
|
break;
|
}
|
}
|
}
|
}
|
return charset;
|
}catch (Exception e) {
|
return null;
|
}finally {
|
if(bis != null) {
|
try {
|
bis.close();
|
} catch (IOException e) {
|
}
|
}
|
}
|
|
}
|
|
/**
|
* 复制文件(带重试和校验-防止中科方德复制文件出错问题)
|
* @param oldPath
|
* @param newPath
|
* @param fileName
|
*/
|
public static boolean copyFile(String oldPath, String newPath, String fileName){
|
// 1. 路径规范化
|
String absolutePathSend = fileUploadFolder + oldPath;
|
String absolutePathTarget = fileUploadFolder + newPath;
|
|
// 2. 创建目标文件夹
|
File targetFolder = new File(absolutePathTarget);
|
if (!targetFolder.exists() && !targetFolder.mkdirs()) {
|
log.error("无法创建目标文件夹: {}", absolutePathTarget);
|
return false;
|
}
|
|
// 3. 源文件和目标文件路径
|
String sourceFilePath = absolutePathSend + File.separator + fileName;
|
String targetFilePath = absolutePathTarget + File.separator + fileName;
|
|
// 4. 获取源文件预期大小
|
long expectedSize = -1;
|
File sourceFile = new File(sourceFilePath);
|
if (!sourceFile.exists()) {
|
log.error("源文件不存在: {}", sourceFilePath);
|
return false;
|
}
|
|
try {
|
expectedSize = sourceFile.length();
|
log.info("文件复制: 源={}, 目标={}, 大小={}字节", sourceFilePath, targetFilePath, expectedSize);
|
} catch (SecurityException e) {
|
log.error("无法访问源文件: {}", sourceFilePath, e);
|
return false;
|
}
|
|
// 5. 复制操作(最多重试3次)
|
int maxRetries = 3;
|
int attempt = 0;
|
boolean copySuccess = false;
|
|
while (attempt < maxRetries && !copySuccess) {
|
attempt++;
|
try {
|
log.info("---- 开始第{}次复制尝试 ----", attempt);
|
|
// 6. 执行复制操作
|
try (FileChannel in = new FileInputStream(sourceFilePath).getChannel();
|
FileChannel out = new FileOutputStream(targetFilePath).getChannel()) {
|
out.transferFrom(in, 0, in.size());
|
}
|
|
// 7. 文件大小验证
|
File copiedFile = new File(targetFilePath);
|
long actualSize = copiedFile.length();
|
|
if (expectedSize != actualSize) {
|
log.warn("大小不一致! 预期: {}B, 实际: {}B (差值: {}B)",
|
expectedSize, actualSize, Math.abs(actualSize - expectedSize));
|
} else {
|
log.info("文件大小验证成功: {}B", actualSize);
|
copySuccess = true;
|
}
|
|
// 8. MD5校验
|
if (copySuccess) {
|
String sourceMd5 = calculateMD5(sourceFilePath);
|
String targetMd5 = calculateMD5(targetFilePath);
|
|
if (!sourceMd5.equals(targetMd5)) {
|
log.warn("MD5不一致! 源: {}, 目标: {}", sourceMd5, targetMd5);
|
copySuccess = false;
|
} else {
|
log.info("MD5验证成功");
|
}
|
}
|
|
} catch (IOException | SecurityException e) {
|
log.error("第{}次复制失败", attempt, e);
|
}
|
|
// 9. 重试间隔处理
|
if (!copySuccess && attempt < maxRetries) {
|
try {
|
int delay = 200 * attempt; // 递增延迟:200ms, 400ms
|
log.info("等待 {}ms 后重试", delay);
|
Thread.sleep(delay);
|
} catch (InterruptedException ie) {
|
Thread.currentThread().interrupt();
|
}
|
}
|
}
|
|
if (copySuccess) {
|
log.info("文件复制完成: {}", targetFilePath);
|
} else {
|
log.error("文件复制失败,已尝试{}次", maxRetries);
|
}
|
|
return copySuccess;
|
}
|
|
/**
|
* 计算文件的MD5值
|
* @param filePath
|
* @return
|
*/
|
public static String calculateMD5(String filePath) {
|
try (InputStream is = Files.newInputStream(Paths.get(filePath))) {
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
byte[] buffer = new byte[8192];
|
int read;
|
|
while ((read = is.read(buffer)) != -1) {
|
md.update(buffer, 0, read);
|
}
|
|
byte[] digest = md.digest();
|
StringBuilder sb = new StringBuilder();
|
for (byte b : digest) {
|
sb.append(String.format("%02x", b));
|
}
|
return sb.toString();
|
} catch (Exception e) {
|
throw new RuntimeException("计算MD5失败: " + filePath, e);
|
}
|
}
|
|
|
public static String getFileAbsPath(String path, String fileName){
|
path = fileUploadFolder + path;
|
File file = new File(path, fileName);
|
if(file.exists()){
|
return file.getAbsolutePath();
|
}
|
return null;
|
}
|
|
|
}
|