feat: 支持下载tar格式
This commit is contained in:
@@ -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, "存在子评论,无法删除");
|
||||
|
||||
@@ -121,6 +121,11 @@
|
||||
<artifactId>agt-spring-boot-starter-monitor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tika</groupId>
|
||||
<artifactId>tika-core</artifactId> <!-- 文件客户端:文件类型的识别 -->
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -253,7 +253,7 @@ public class LicenseController {
|
||||
})
|
||||
@PreAuthorize("@ss.hasPermission('license:license:import')")
|
||||
public CommonResult<ImportRespVO> 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<LicenseCodeImportExcelVO> 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);
|
||||
}
|
||||
}
|
||||
@@ -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> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<LicenseCodeImportExcelVO> list, Boolean updateSupport);
|
||||
|
||||
void updateDetailById(LicenseDetailVO licenseDetail);
|
||||
|
||||
void download(HttpServletResponse response, Long id);
|
||||
}
|
||||
@@ -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<LicenseDetailDO> detailList = licenseDetailMapper.selectList(Wrappers.<LicenseDetailDO>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<byte[]> result = fileApi.getFileContent(fileUrl);
|
||||
byte[] fileContent = result.getData();
|
||||
archiver.add(FileUtil.writeBytes(fileContent, tempFilePath + fileName));
|
||||
FileUtil.del(tempFilePath + fileName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user