[파일 업로드] tusHook 작업

- 멀티업로드 상태 업로드
This commit is contained in:
2026-03-06 17:45:46 +09:00
parent 5311c816d2
commit 8ae793626b
7 changed files with 131 additions and 52 deletions
@@ -102,21 +102,9 @@ public class FileController {
@PostMapping("/tusHook")
public ResponseEntity<ApiResponse<String>> tusHook(@RequestBody TusHookForm tusHookForm) {
// log.info("tusHookForm: {}", tusHookForm);
// fileService.insertFileAndSave(tusHookForm.tusHookDto());
log.info("[tusHook] type={}", tusHookForm.getType());
log.info("[tusHook] uploadId={}", tusHookForm.getTusHookEventForm().getTusHookUploadForm().getId());
log.info("[tusHook] size={}", tusHookForm.getTusHookEventForm().getTusHookUploadForm().getSize());
log.info("[tusHook] metaData={}", tusHookForm.getTusHookEventForm().getTusHookUploadForm().getMetaData());
log.info("[tusHookDto] type={}", tusHookForm.tusHookDto().getType());
log.info("[tusHookDto] uploadId={}", tusHookForm.tusHookDto().getUploadId());
log.info("[tusHookDto] size={}", tusHookForm.tusHookDto().getSize());
log.info("[tusHookDto] fileUuid={}", tusHookForm.tusHookDto().getFileUuid());
log.info("[tusHookDto] filename={}", tusHookForm.tusHookDto().getFilename());
log.info("[tusHookDto] filetype={}", tusHookForm.tusHookDto().getFiletype());
fileService.saveUploadStatusFromTusHook(tusHookForm.tusHookDto());
fileService.updateFileUploadStatus(tusHookForm.tusHookDto());
fileService.saveUploadStatusRedisTusHook(tusHookForm.tusHookDto());
return ApiResponse.entity("", ApiResponseCode.CODE_200);
}
@@ -13,4 +13,6 @@ public class TusHookDto {
private String fileUuid;
private String filename;
private String filetype;
private long uploadedBytes;
private int status;
}
@@ -6,7 +6,7 @@ import lombok.Setter;
@Getter
@Setter
public class UploadStatusDto {
private String fileMasterKey;
private String fileUuid;
private String status; // PENDING, UPLOADING, DONE, FAILED
private long uploadedBytes;
private long totalBytes;
@@ -9,11 +9,11 @@ import lombok.Setter;
@Setter
public class UploadStatusForm {
@NotBlank
private String fileMasterKey;
private String fileUuid;
public UploadStatusDto uploadStatusDto() {
UploadStatusDto uploadStatusDto = new UploadStatusDto();
uploadStatusDto.setFileMasterKey(this.fileMasterKey == null ? null : this.fileMasterKey.trim());
uploadStatusDto.setFileUuid(this.fileUuid == null ? null : this.fileUuid.trim());
return uploadStatusDto;
}
}
@@ -1,9 +1,6 @@
package com.alist.api.common.modules.file.mapper;
import com.alist.api.common.modules.file.dto.FileUploadDto;
import com.alist.api.common.modules.file.dto.FileUploadItemDto;
import com.alist.api.common.modules.file.dto.UploadStartDto;
import com.alist.api.common.modules.file.dto.UploadTokenDto;
import com.alist.api.common.modules.file.dto.*;
import org.apache.ibatis.annotations.Mapper;
@Mapper
@@ -17,4 +14,10 @@ public interface FileMapper {
void insertFileDetailInit(FileUploadItemDto item);
int selectFileDetailCountByFileUuidUserToKenIdx(UploadTokenDto uploadTokenDto);
void insertFileUploadEventLog(TusHookDto tusHookDto);
void updateFileDetailStatus(TusHookDto tusHookDto);
void updateFileMasterAggregateByFileUuid(String fileUuid);
}
@@ -119,40 +119,62 @@ public class FileService {
return uploadTokenDto;
}
public UploadStartDto insertFileMasterUpload(Integer userIdx, Integer userTokenIdx, UploadStartDto uploadStartDto) {
uploadStartDto.setFileMasterKey(UUID.randomUUID().toString());
uploadStartDto.setUserIdx(userIdx);
uploadStartDto.setStatus(1);
uploadStartDto.setFolderPath(uploadStartDto.getFolderPath() == null ? "/" : uploadStartDto.getFolderPath());
fileMapper.insertFileMasterUpload(uploadStartDto);
uploadStartDto.setUploadToken(jwtTokenProvider.createUploadToken(userTokenIdx));
uploadStartDto.setTusEndpoint(tusEndpoint);
return uploadStartDto;
}
public UploadStatusDto getUploadStatus(UploadStatusDto uploadStatusDto) {
String key = buildUploadStatusKey(uploadStatusDto.getFileMasterKey());
String key = buildUploadStatusKey(uploadStatusDto.getFileUuid());
Map<Object, Object> map = stringRedisTemplate.opsForHash().entries(key);
if (map.isEmpty()) {
return null;
}
UploadStatusDto dto = new UploadStatusDto();
dto.setFileMasterKey((String) map.getOrDefault("fileMasterKey", uploadStatusDto.getFileMasterKey()));
dto.setStatus((String) map.getOrDefault("status", "PENDING"));
dto.setUploadedBytes(parseLong(map.get("uploadedBytes")));
dto.setTotalBytes(parseLong(map.get("totalBytes")));
dto.setPercent((int) parseLong(map.get("percent")));
dto.setUpdatedAt((String) map.getOrDefault("updatedAt", ""));
uploadStatusDto.setFileUuid((String) map.getOrDefault("fileUuid", uploadStatusDto.getFileUuid()));
uploadStatusDto.setStatus((String) map.getOrDefault("status", "PENDING"));
uploadStatusDto.setUploadedBytes(parseLong(map.get("uploadedBytes")));
uploadStatusDto.setTotalBytes(parseLong(map.get("totalBytes")));
uploadStatusDto.setPercent((int) parseLong(map.get("percent")));
uploadStatusDto.setUpdatedAt((String) map.getOrDefault("updatedAt", ""));
return dto;
return uploadStatusDto;
}
public void saveUploadStatusFromTusHook(TusHookDto tusHookDto) {
/*db에 상태값 저장*/
public void updateFileUploadStatus(TusHookDto tusHookDto) {
if (tusHookDto == null || tusHookDto.getFileUuid() == null || tusHookDto.getFileUuid().isBlank()) {
return;
}
String type = tusHookDto.getType() == null ? "" : tusHookDto.getType().trim().toLowerCase();
long offset = tusHookDto.getOffset() == null ? 0L : tusHookDto.getOffset();
long size = tusHookDto.getSize() == null ? 0L : tusHookDto.getSize();
fileMapper.insertFileUploadEventLog(tusHookDto);
tusHookDto.setUploadedBytes(offset);
switch (type) {
case "pre-create", "post-receive":
tusHookDto.setStatus(1); // UPLOADING
break;
case "post-finish":
tusHookDto.setStatus(3); // DONE
tusHookDto.setUploadedBytes(size);
break;
case "post-terminate":
tusHookDto.setStatus(5); // CANCELED (정책에 따라 FAILED=4)
break;
default:
return;
}
fileMapper.updateFileDetailStatus(tusHookDto);
if ("post-finish".equals(type) || "post-terminate".equals(type)) {
fileMapper.updateFileMasterAggregateByFileUuid(tusHookDto.getFileUuid());
}
}
/*레이디스에 상태값 저장*/
public void saveUploadStatusRedisTusHook(TusHookDto tusHookDto) {
if (tusHookDto == null || tusHookDto.getFileUuid() == null || tusHookDto.getFileUuid().isBlank()) {
return;
}
@@ -186,10 +208,6 @@ public class FileService {
stringRedisTemplate.expire(key, Duration.ofHours(24));
}
public void insertFileAndSave(TusHookDto tusHookDto) {
}
private String buildUploadStatusKey(String fileUuid) {
return "upload:alist:status:" + fileUuid;
}
@@ -217,7 +235,7 @@ public class FileService {
}
}
private void evictUploadAuthCacheByFileMasterKey(String fileUuid) {
private void evictUploadAuthCacheByFileUuid(String fileUuid) {
try {
String pattern = "upload:alist:auth:" + fileUuid + ":*";
var keys = stringRedisTemplate.keys(pattern);
@@ -30,6 +30,74 @@
)
</insert>
<insert id="insertFileUploadEventLog">
/*FileMapper.insertFileUploadEventLog*/
INSERT INTO ALISTLMS.FILE_UPLOAD_EVENT_LOG (
FILE_DETAIL_IDX, EVENT_TYPE, OFFSET_BYTES, BYTES_WRITTEN, HTTP_STATUS, MESSAGE, RAW_JSON
)
SELECT
LFD.FILE_DETAIL_IDX
, #{type}
, #{offset}
, NULL
, NULL
, NULL
, NULL
FROM ALISTLMS.FILE_DETAIL LFD
WHERE LFD.FILE_UUID = #{fileUuid}
LIMIT 1
</insert>
<update id="updateFileDetailStatus">
/*FileMapper.updateFileDetailStatus*/
UPDATE ALISTLMS.FILE_DETAIL
<set>
<if test="uploadId != null and uploadId != ''">
TUS_UPLOAD_ID = #{uploadId},
</if>
<if test="uploadedBytes != null">
UPLOADED_BYTES = #{uploadedBytes},
</if>
<if test="status != null">
STATUS = #{status},
</if>
UPDATE_DATE = NOW()
</set>
WHERE FILE_UUID = #{fileUuid}
AND DEL_YN = 'N'
</update>
<update id="updateFileMasterAggregateByFileUuid">
/*FileMapper.updateFileMasterAggregateByFileUuid*/
UPDATE ALISTLMS.FILE_MASTER LFM
JOIN (
SELECT
LLFD.FILE_MASTER_IDX,
COUNT(*) AS total_count,
SUM(CASE WHEN LLFD.STATUS = 3 THEN 1 ELSE 0 END) AS done_count,
SUM(CASE WHEN LLFD.STATUS IN (4,5) THEN 1 ELSE 0 END) AS fail_count
FROM ALISTLMS.FILE_DETAIL LLFD
WHERE LLFD.FILE_MASTER_IDX = (
SELECT FILE_MASTER_IDX
FROM ALISTLMS.FILE_DETAIL
WHERE FILE_UUID = #{fileUuid}
LIMIT 1
)
AND LLFD.DEL_YN = 'N'
) A ON A.FILE_MASTER_IDX = LFM.FILE_MASTER_IDX
SET LFM.TOTAL_COUNT = A.total_count,
LFM.DONE_COUNT = A.done_count,
LFM.FAIL_COUNT = A.fail_count,
LFM.STATUS = CASE
WHEN A.done_count = A.total_count THEN 3
WHEN A.done_count = 0 AND A.fail_count = A.total_count THEN 4
WHEN A.done_count > 0 AND A.fail_count > 0 THEN 5
ELSE 1
END,
LFM.UPDATE_DATE = NOW()
</update>
<select id="selectFileMasterCountByFileMasterKeyAndDelYnAndStatus" resultType="int">
/*FileMapper.selectFileMasterCountByFileMasterKeyAndDelYnAndStatus*/
SELECT COUNT(*)