diff --git a/agt-module-license/agt-module-license-api/src/main/java/org/agt/module/license/enums/ErrorCodeConstants.java b/agt-module-license/agt-module-license-api/src/main/java/org/agt/module/license/enums/ErrorCodeConstants.java
index 54fc6a3..fae139c 100644
--- a/agt-module-license/agt-module-license-api/src/main/java/org/agt/module/license/enums/ErrorCodeConstants.java
+++ b/agt-module-license/agt-module-license-api/src/main/java/org/agt/module/license/enums/ErrorCodeConstants.java
@@ -19,6 +19,7 @@ public interface ErrorCodeConstants {
ErrorCode LICENSE_NOT_EXISTS = new ErrorCode(1_100_003_001, "License不存在");
ErrorCode LICENSE_SN_DUPLICATE = new ErrorCode(1_100_003_002, "License SN`{}`已存在");
ErrorCode LICENSE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_100_003_003, "导入License数据不能为空!");
+ ErrorCode LICENSE_DOWNLOAD_FAILED = new ErrorCode(1_100_003_004, "下载失败!");
ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1_100_004_001, "评论不存在");
ErrorCode COMMENT_EXITS_CHILDREN = new ErrorCode(1_100_004_002, "存在子评论,无法删除");
diff --git a/agt-module-license/agt-module-license-server/pom.xml b/agt-module-license/agt-module-license-server/pom.xml
index c728b7c..85a965f 100644
--- a/agt-module-license/agt-module-license-server/pom.xml
+++ b/agt-module-license/agt-module-license-server/pom.xml
@@ -121,6 +121,11 @@
agt-spring-boot-starter-monitor
+
+ org.apache.tika
+ tika-core
+
+
diff --git a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/LicenseController.java b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/LicenseController.java
index 3825907..e436e1e 100644
--- a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/LicenseController.java
+++ b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/controller/admin/license/LicenseController.java
@@ -253,7 +253,7 @@ public class LicenseController {
})
@PreAuthorize("@ss.hasPermission('license:license:import')")
public CommonResult importCodeExcel(@RequestParam("file") MultipartFile file,
- @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
+ @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
List list = ExcelUtils.read(file, LicenseCodeImportExcelVO.class);
return success(licenseService.importCodeList(list, updateSupport));
}
@@ -265,4 +265,12 @@ public class LicenseController {
licenseService.updateDetailById(updateReqVO);
return success(true);
}
+
+ @Operation(summary = "下载")
+ @GetMapping("/download")
+ @Parameter(name = "tableId", description = "id", required = true, example = "1024")
+ public void download(@RequestParam("id") Long id,
+ HttpServletResponse response) {
+ licenseService.download(response, id);
+ }
}
\ No newline at end of file
diff --git a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/framework/util/FileTypeUtils.java b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/framework/util/FileTypeUtils.java
new file mode 100644
index 0000000..2055673
--- /dev/null
+++ b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/framework/util/FileTypeUtils.java
@@ -0,0 +1,97 @@
+package org.agt.module.license.framework.util;
+
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.ttl.TransmittableThreadLocal;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.agt.framework.common.util.http.HttpUtils;
+import org.apache.tika.Tika;
+import org.apache.tika.mime.MimeTypeException;
+import org.apache.tika.mime.MimeTypes;
+
+import java.io.IOException;
+
+/**
+ * 文件类型 Utils
+ *
+ * @author 千通源码
+ */
+@Slf4j
+public class FileTypeUtils {
+
+ private static final ThreadLocal TIKA = TransmittableThreadLocal.withInitial(Tika::new);
+
+ /**
+ * 获得文件的 mineType,对于 doc,jar 等文件会有误差
+ *
+ * @param data 文件内容
+ * @return mineType 无法识别时会返回“application/octet-stream”
+ */
+ @SneakyThrows
+ public static String getMineType(byte[] data) {
+ return TIKA.get().detect(data);
+ }
+
+ /**
+ * 已知文件名,获取文件类型,在某些情况下比通过字节数组准确,例如使用 jar 文件时,通过名字更为准确
+ *
+ * @param name 文件名
+ * @return mineType 无法识别时会返回“application/octet-stream”
+ */
+ public static String getMineType(String name) {
+ return TIKA.get().detect(name);
+ }
+
+ /**
+ * 在拥有文件和数据的情况下,最好使用此方法,最为准确
+ *
+ * @param data 文件内容
+ * @param name 文件名
+ * @return mineType 无法识别时会返回“application/octet-stream”
+ */
+ public static String getMineType(byte[] data, String name) {
+ return TIKA.get().detect(data, name);
+ }
+
+ /**
+ * 根据 mineType 获得文件后缀
+ *
+ * 注意:如果获取不到,或者发生异常,都返回 null
+ *
+ * @param mineType 类型
+ * @return 后缀,例如说 .pdf
+ */
+ public static String getExtension(String mineType) {
+ try {
+ return MimeTypes.getDefaultMimeTypes().forName(mineType).getExtension();
+ } catch (MimeTypeException e) {
+ log.warn("[getExtension][获取文件后缀({}) 失败]", mineType, e);
+ return null;
+ }
+ }
+
+ /**
+ * 返回附件
+ *
+ * @param response 响应
+ * @param filename 文件名
+ * @param content 附件内容
+ */
+ public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
+ // 设置 header 和 contentType
+ response.setHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename));
+ String contentType = getMineType(content, filename);
+ response.setContentType(contentType);
+ // 针对 video 的特殊处理,解决视频地址在移动端播放的兼容性问题
+ if (StrUtil.containsIgnoreCase(contentType, "video")) {
+ response.setHeader("Content-Length", String.valueOf(content.length - 1));
+ response.setHeader("Content-Range", String.valueOf(content.length - 1));
+ response.setHeader("Accept-Ranges", "bytes");
+ }
+ // 输出附件
+ IoUtil.write(response.getOutputStream(), false, content);
+ }
+
+}
diff --git a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseService.java b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseService.java
index 43af9fb..df8814c 100644
--- a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseService.java
+++ b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseService.java
@@ -1,5 +1,6 @@
package org.agt.module.license.service.license;
+import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.agt.framework.common.pojo.PageResult;
import org.agt.module.license.controller.admin.license.vo.ImportRespVO;
@@ -99,4 +100,6 @@ public interface LicenseService {
ImportRespVO importCodeList(List list, Boolean updateSupport);
void updateDetailById(LicenseDetailVO licenseDetail);
+
+ void download(HttpServletResponse response, Long id);
}
\ No newline at end of file
diff --git a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseServiceImpl.java b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseServiceImpl.java
index 30feb8e..fd75e6e 100644
--- a/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseServiceImpl.java
+++ b/agt-module-license/agt-module-license-server/src/main/java/org/agt/module/license/service/license/LicenseServiceImpl.java
@@ -3,13 +3,18 @@ package org.agt.module.license.service.license;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
+import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
+import cn.hutool.extra.compress.CompressUtil;
+import cn.hutool.extra.compress.archiver.Archiver;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
+import org.agt.framework.common.pojo.CommonResult;
import org.agt.framework.common.pojo.PageResult;
import org.agt.framework.common.util.object.BeanUtils;
import org.agt.framework.dict.core.DictFrameworkUtils;
@@ -37,17 +42,21 @@ import org.agt.module.license.dal.mysql.license.LicenseMapper;
import org.agt.module.license.dal.mysql.license.LicenseProviderMapper;
import org.agt.module.license.dal.mysql.project.ProjectMapper;
import org.agt.module.license.enums.LicenseStatusEnum;
+import org.agt.module.license.framework.util.FileTypeUtils;
import org.agt.module.system.api.mail.MailSendApi;
import org.agt.module.system.api.mail.dto.MailSendSingleToUserReqDTO;
import org.agt.module.system.api.notify.NotifyMessageSendApi;
import org.agt.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
+import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
@@ -61,6 +70,7 @@ import java.util.Optional;
import java.util.stream.Collectors;
import static org.agt.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static org.agt.module.license.enums.ErrorCodeConstants.LICENSE_DOWNLOAD_FAILED;
import static org.agt.module.license.enums.ErrorCodeConstants.LICENSE_IMPORT_LIST_IS_EMPTY;
import static org.agt.module.license.enums.ErrorCodeConstants.LICENSE_NOT_EXISTS;
import static org.agt.module.license.enums.ErrorCodeConstants.LICENSE_SN_DUPLICATE;
@@ -408,6 +418,7 @@ public class LicenseServiceImpl implements LicenseService {
// File tempFile = new File("D:/temp/temp.zip");
File tempFile = new File("/usr/local/licGen/temp.zip");
+
ZipUtil.zip(tempFile, pathList.toArray(new String[0]), inputStreamList.toArray(new ByteArrayInputStream[inputStreamList.size()]));
String fileURL = fileApi.createFile(FileUtil.readBytes(tempFile), licenseDO.getSerialNo() + "_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + ".zip");
@@ -988,4 +999,35 @@ public class LicenseServiceImpl implements LicenseService {
}
licenseDetailMapper.updateById(licenseDetailDO);
}
+
+ public void download(HttpServletResponse response, Long id) {
+ try {
+ List detailList = licenseDetailMapper.selectList(Wrappers.lambdaQuery()
+ .eq(LicenseDetailDO::getLicenseId, id));
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ Archiver archiver = CompressUtil.createArchiver(CharsetUtil.CHARSET_UTF_8, ArchiveStreamFactory.TAR, outputStream);
+
+ for (LicenseDetailDO detail : detailList) {
+ addArchiverFile(archiver, detail.getFileUrl());
+ addArchiverFile(archiver, detail.getFileUrlLegacy());
+ }
+ archiver.finish().close();
+ FileTypeUtils.writeAttachment(response, "temp.tar", outputStream.toByteArray());
+ } catch (IOException e) {
+ throw exception(LICENSE_DOWNLOAD_FAILED);
+ }
+ }
+
+ private void addArchiverFile(Archiver archiver, String fileUrl) {
+ String tempFilePath = "/usr/local/licGen/";
+ if (StrUtil.isNotBlank(fileUrl)) {
+ String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1, fileUrl.lastIndexOf("_")) + ".ini";
+ CommonResult result = fileApi.getFileContent(fileUrl);
+ byte[] fileContent = result.getData();
+ archiver.add(FileUtil.writeBytes(fileContent, tempFilePath + fileName));
+ FileUtil.del(tempFilePath + fileName);
+ }
+ }
+
}
\ No newline at end of file