|
|
@@ -0,0 +1,214 @@
|
|
|
+package cn.reghao.tnb.file.app.service;
|
|
|
+
|
|
|
+import cn.reghao.jutil.jdk.converter.ByteConverter;
|
|
|
+import cn.reghao.jutil.jdk.converter.ByteType;
|
|
|
+import cn.reghao.jutil.jdk.security.DigestUtil;
|
|
|
+import cn.reghao.jutil.jdk.web.db.PageList;
|
|
|
+import cn.reghao.tnb.common.auth.UserContext;
|
|
|
+import cn.reghao.tnb.common.web.ServletUtil;
|
|
|
+import cn.reghao.tnb.file.app.config.AppProperties;
|
|
|
+import cn.reghao.tnb.file.app.db.mapper.LocalFileMapper;
|
|
|
+import cn.reghao.tnb.file.app.model.po.LocalFile;
|
|
|
+import cn.reghao.tnb.file.app.model.vo.LocalFileInfo;
|
|
|
+import cn.reghao.tnb.file.app.model.vo.LocalFileUrl;
|
|
|
+import jakarta.servlet.http.HttpServletResponse;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.io.FileUtils;
|
|
|
+import org.springframework.cache.annotation.Cacheable;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+
|
|
|
+import java.io.*;
|
|
|
+import java.nio.file.Files;
|
|
|
+import java.nio.file.Path;
|
|
|
+import java.nio.file.StandardCopyOption;
|
|
|
+import java.util.List;
|
|
|
+import java.util.UUID;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author reghao
|
|
|
+ * @date 2024-01-19 21:29:24
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class FileService {
|
|
|
+ // 8MiB
|
|
|
+ private final int bufSize = 1024*1024*8;
|
|
|
+ private final ByteConverter byteConverter;
|
|
|
+ private final LocalFileMapper localFileMapper;
|
|
|
+ private int pageSize = 10;
|
|
|
+ private String baseDir;
|
|
|
+
|
|
|
+ public FileService(ByteConverter byteConverter, LocalFileMapper localFileMapper, AppProperties appProperties) {
|
|
|
+ this.byteConverter = byteConverter;
|
|
|
+ this.localFileMapper = localFileMapper;
|
|
|
+ this.baseDir = appProperties.getBaseDir();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void getFile(String objectName) throws IOException {
|
|
|
+ LocalFile diskFile = getByObjectName(objectName);
|
|
|
+ if (diskFile == null) {
|
|
|
+ log.error("LocalFile {} not exist", objectName);
|
|
|
+ writeResponse(HttpServletResponse.SC_NOT_FOUND);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String absolutePath = diskFile.getAbsolutePath();
|
|
|
+ File file = new File(absolutePath);
|
|
|
+ if (!file.exists() || file.isDirectory()) {
|
|
|
+ log.error("file {} not exist", absolutePath);
|
|
|
+ writeResponse(HttpServletResponse.SC_NOT_FOUND);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String contentType = diskFile.getContentType();
|
|
|
+ long size = diskFile.getSize();
|
|
|
+ HttpServletResponse response = ServletUtil.getResponse();
|
|
|
+ response.setStatus(HttpServletResponse.SC_OK);
|
|
|
+ response.setContentType(contentType);
|
|
|
+ response.setContentLengthLong(size);
|
|
|
+
|
|
|
+ OutputStream outputStream = response.getOutputStream();
|
|
|
+ writeResponse(outputStream, absolutePath, 0, size);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Cacheable(cacheNames = "filePaths", key = "#objectName", unless = "#result == null")
|
|
|
+ public LocalFile getByObjectName(String objectName) {
|
|
|
+ LocalFile diskFile = localFileMapper.findByObjectName(objectName);
|
|
|
+ return diskFile;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void writeResponse(int statusCode) throws IOException {
|
|
|
+ HttpServletResponse response = ServletUtil.getResponse();
|
|
|
+ response.setStatus(statusCode);
|
|
|
+ OutputStream outputStream = response.getOutputStream();
|
|
|
+ outputStream.flush();
|
|
|
+ outputStream.close();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void writeResponse(OutputStream outputStream, String absolutePath, long start, long end) throws IOException {
|
|
|
+ RandomAccessFile raf = new RandomAccessFile(absolutePath, "r");
|
|
|
+ raf.seek(start);
|
|
|
+
|
|
|
+ long len = end-start+1;
|
|
|
+ if (len < bufSize) {
|
|
|
+ int len1 = (int) len;
|
|
|
+ byte[] buf1 = new byte[len1];
|
|
|
+ int readLen1 = raf.read(buf1, 0, len1);
|
|
|
+ outputStream.write(buf1, 0, readLen1);
|
|
|
+ } else {
|
|
|
+ byte[] buf = new byte[bufSize];
|
|
|
+ long totalRead = 0;
|
|
|
+ int readLen;
|
|
|
+ while ((readLen = raf.read(buf, 0, bufSize)) != -1) {
|
|
|
+ outputStream.write(buf, 0, readLen);
|
|
|
+ totalRead += readLen;
|
|
|
+
|
|
|
+ long left = len - totalRead;
|
|
|
+ if (left < bufSize) {
|
|
|
+ int left1 = (int) left;
|
|
|
+ byte[] buf1 = new byte[left1];
|
|
|
+ int readLen1 = raf.read(buf1, 0, left1);
|
|
|
+ outputStream.write(buf1, 0, readLen1);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ outputStream.flush();
|
|
|
+ outputStream.close();
|
|
|
+ raf.close();
|
|
|
+ }
|
|
|
+
|
|
|
+ public LocalFile putLocalFile(MultipartFile file) throws Exception {
|
|
|
+ long size = file.getSize();
|
|
|
+ String filename = file.getOriginalFilename();
|
|
|
+ String suffix = getSuffix(filename);
|
|
|
+
|
|
|
+ String objectId = UUID.randomUUID().toString().replace("-", "");;
|
|
|
+ String objectName;
|
|
|
+ if (suffix.isBlank()) {
|
|
|
+ objectName = String.format("file/%s", objectId);
|
|
|
+ } else {
|
|
|
+ objectName = String.format("file/%s%s", objectId, suffix);
|
|
|
+ }
|
|
|
+
|
|
|
+ String contentId = UUID.randomUUID().toString().replace("-", "");
|
|
|
+ String destPath = String.format("%s/%s", baseDir, contentId);
|
|
|
+ File savedFile = saveFile(file.getInputStream(), destPath);
|
|
|
+ String contentType = "image/jpeg";
|
|
|
+ int fileType = 1001;
|
|
|
+ String sha256sum = DigestUtil.sha256sum(savedFile.getAbsolutePath());
|
|
|
+
|
|
|
+ List<LocalFile> diskFiles = localFileMapper.findBySha256sum(sha256sum);
|
|
|
+ LocalFile diskFile;
|
|
|
+ if (!diskFiles.isEmpty()) {
|
|
|
+ LocalFile existFile = diskFiles.get(0);
|
|
|
+ diskFile = new LocalFile(objectName, objectId, existFile, filename);
|
|
|
+ FileUtils.deleteQuietly(savedFile);
|
|
|
+ } else {
|
|
|
+ diskFile = new LocalFile(objectName, objectId, savedFile.getAbsolutePath(), sha256sum, filename, contentType, fileType, size);
|
|
|
+ }
|
|
|
+
|
|
|
+ localFileMapper.save(diskFile);
|
|
|
+ return diskFile;
|
|
|
+ }
|
|
|
+
|
|
|
+ private File saveFile(InputStream inputStream, String absolutePath) throws IOException {
|
|
|
+ File file = new File(absolutePath);
|
|
|
+ if (file.exists()) {
|
|
|
+ throw new IOException(absolutePath + " exist");
|
|
|
+ }
|
|
|
+
|
|
|
+ Files.copy(inputStream, Path.of(absolutePath), StandardCopyOption.REPLACE_EXISTING);
|
|
|
+ return file;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getSuffix(String filename) {
|
|
|
+ if (filename == null) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ int idx = filename.lastIndexOf(".");
|
|
|
+ return idx == -1 ? "" : filename.substring(idx);
|
|
|
+ }
|
|
|
+
|
|
|
+ public PageList<LocalFileInfo> getLocalFiles(int pageNumber) {
|
|
|
+ long owner = UserContext.getUserId();
|
|
|
+ List<LocalFile> list = localFileMapper.findByOwner(owner);
|
|
|
+ List<LocalFileInfo> list0 = list.stream().map(diskFile -> {
|
|
|
+ String size = byteConverter.convert(ByteType.Bytes, diskFile.getSize());
|
|
|
+ return new LocalFileInfo(diskFile, size);
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ return PageList.empty();
|
|
|
+ }
|
|
|
+
|
|
|
+ public PageList<LocalFileUrl> getImageFiles(int pageNumber) {
|
|
|
+ long owner = UserContext.getUserId();
|
|
|
+ int fileType = 1001;
|
|
|
+ List<LocalFile> list = localFileMapper.findByFileTypeAndOwner(fileType, owner);
|
|
|
+ List<LocalFileUrl> list0 = list.stream().map(diskFile -> {
|
|
|
+ String fileId = diskFile.getObjectId();
|
|
|
+ String filename = diskFile.getFilename();
|
|
|
+ String objectName = diskFile.getObjectName();
|
|
|
+ String url = String.format("/%s", objectName);
|
|
|
+ return new LocalFileUrl(fileId, filename, url);
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+
|
|
|
+ return PageList.empty();
|
|
|
+ }
|
|
|
+
|
|
|
+ public LocalFile getLocalFile(String objectId) {
|
|
|
+ return localFileMapper.findByObjectId(objectId);
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getObjectName(String path) {
|
|
|
+ String objectName = "";
|
|
|
+ if (!path.equals("/")) {
|
|
|
+ objectName = path.substring(1) + "/";
|
|
|
+ }
|
|
|
+
|
|
|
+ return objectName;
|
|
|
+ }
|
|
|
+}
|