[파일 업로드] tusHook 작업
- 실시간 상태 조회
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package com.alist.api.common.modules.file;
|
||||
|
||||
import com.alist.api.common.modules.file.dto.UploadStatusDto;
|
||||
import com.alist.api.common.modules.file.dto.UploadTokenDto;
|
||||
import com.alist.api.common.modules.file.form.TusHookForm;
|
||||
import com.alist.api.common.modules.file.form.UploadStatusForm;
|
||||
import com.alist.api.common.response.ApiResponse;
|
||||
import com.alist.api.common.response.ApiResponseCode;
|
||||
import com.alist.api.common.modules.file.dto.UploadStartDto;
|
||||
@@ -82,6 +84,17 @@ public class FileController {
|
||||
return ApiResponse.entity("", ApiResponseCode.CODE_200);
|
||||
}
|
||||
|
||||
@PostMapping("/uploadStatus")
|
||||
public ResponseEntity<ApiResponse<UploadStatusDto>> uploadStatus(
|
||||
@Valid @RequestBody UploadStatusForm uploadStatusForm
|
||||
) {
|
||||
UploadStatusDto status = fileService.getUploadStatus(uploadStatusForm.uploadStatusDto());
|
||||
if (status == null) {
|
||||
return ApiResponse.entity(new UploadStatusDto(), ApiResponseCode.CODE_2003);
|
||||
}
|
||||
return ApiResponse.entity(status, ApiResponseCode.CODE_200);
|
||||
}
|
||||
|
||||
@PostMapping("/tusHook")
|
||||
public ResponseEntity<ApiResponse<String>> tusHook(@RequestBody TusHookForm tusHookForm) {
|
||||
// log.info("tusHookForm: {}", tusHookForm);
|
||||
@@ -98,6 +111,8 @@ public class FileController {
|
||||
log.info("[tusHookDto] filename={}", tusHookForm.tusHookDto().getFilename());
|
||||
log.info("[tusHookDto] filetype={}", tusHookForm.tusHookDto().getFiletype());
|
||||
|
||||
fileService.saveUploadStatusFromTusHook(tusHookForm.tusHookDto());
|
||||
|
||||
return ApiResponse.entity("", ApiResponseCode.CODE_200);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ public class TusHookDto {
|
||||
private String type;
|
||||
private String uploadId;
|
||||
private Long size;
|
||||
private Long offset;
|
||||
private String fileMasterKey;
|
||||
private String filename;
|
||||
private String filetype;
|
||||
|
||||
@@ -12,17 +12,17 @@ public class UploadStartDto {
|
||||
private String uploadToken;
|
||||
|
||||
@JsonIgnore
|
||||
String originName;
|
||||
private String originName;
|
||||
@JsonIgnore
|
||||
String contentType;
|
||||
private String contentType;
|
||||
@JsonIgnore
|
||||
Long sizeBytes;
|
||||
private Long sizeBytes;
|
||||
@JsonIgnore
|
||||
String fileCategory;
|
||||
private String fileCategory;
|
||||
@JsonIgnore
|
||||
String folderPath;
|
||||
private String folderPath;
|
||||
@JsonIgnore
|
||||
Integer userIdx;
|
||||
private Integer userIdx;
|
||||
@JsonIgnore
|
||||
int status;
|
||||
private int status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.alist.api.common.modules.file.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class UploadStatusDto {
|
||||
private String fileMasterKey;
|
||||
private String status; // PENDING, UPLOADING, DONE, FAILED
|
||||
private long uploadedBytes;
|
||||
private long totalBytes;
|
||||
private int percent;
|
||||
private String updatedAt;
|
||||
}
|
||||
@@ -17,24 +17,25 @@ public class TusHookForm {
|
||||
private TusHookEventForm tusHookEventForm;
|
||||
|
||||
public TusHookDto tusHookDto() {
|
||||
TusHookDto dto = new TusHookDto();
|
||||
TusHookDto tusHookDto = new TusHookDto();
|
||||
|
||||
dto.setType(this.type);
|
||||
tusHookDto.setType(this.type);
|
||||
|
||||
if (this.tusHookEventForm == null || this.tusHookEventForm.getTusHookUploadForm() == null) {
|
||||
return dto;
|
||||
return tusHookDto;
|
||||
}
|
||||
|
||||
TusHookUploadForm upload = this.tusHookEventForm.getTusHookUploadForm();
|
||||
dto.setUploadId(upload.getId());
|
||||
dto.setSize(upload.getSize());
|
||||
tusHookDto.setUploadId(upload.getId());
|
||||
tusHookDto.setSize(upload.getSize());
|
||||
tusHookDto.setOffset(upload.getOffset());
|
||||
|
||||
if (upload.getMetaData() != null) {
|
||||
dto.setFileMasterKey(upload.getMetaData().get("fileMasterKey"));
|
||||
dto.setFilename(upload.getMetaData().get("filename"));
|
||||
dto.setFiletype(upload.getMetaData().get("filetype"));
|
||||
tusHookDto.setFileMasterKey(upload.getMetaData().get("fileMasterKey"));
|
||||
tusHookDto.setFilename(upload.getMetaData().get("filename"));
|
||||
tusHookDto.setFiletype(upload.getMetaData().get("filetype"));
|
||||
}
|
||||
|
||||
return dto;
|
||||
return tusHookDto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ public class TusHookUploadForm {
|
||||
@JsonProperty("Size")
|
||||
private Long size;
|
||||
|
||||
@JsonProperty("Offset")
|
||||
private Long offset;
|
||||
|
||||
@JsonProperty("MetaData")
|
||||
private Map<String, String> metaData;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.alist.api.common.modules.file.form;
|
||||
|
||||
import com.alist.api.common.modules.file.dto.UploadStatusDto;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class UploadStatusForm {
|
||||
@NotBlank
|
||||
private String fileMasterKey;
|
||||
|
||||
public UploadStatusDto uploadStatusDto() {
|
||||
UploadStatusDto uploadStatusDto = new UploadStatusDto();
|
||||
uploadStatusDto.setFileMasterKey(this.fileMasterKey == null ? null : this.fileMasterKey.trim());
|
||||
return uploadStatusDto;
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,13 @@ package com.alist.api.common.modules.file.service;
|
||||
|
||||
import com.alist.api.common.modules.file.dto.TusHookDto;
|
||||
import com.alist.api.common.modules.file.dto.UploadStartDto;
|
||||
import com.alist.api.common.modules.file.dto.UploadStatusDto;
|
||||
import com.alist.api.common.modules.file.dto.UploadTokenDto;
|
||||
import com.alist.api.common.modules.file.mapper.FileMapper;
|
||||
import com.alist.api.config.jwt.JwtTokenProvider;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -14,7 +16,9 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.HexFormat;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@@ -105,10 +109,76 @@ public class FileService {
|
||||
return uploadTokenDto;
|
||||
}
|
||||
|
||||
public UploadStatusDto getUploadStatus(UploadStatusDto uploadStatusDto) {
|
||||
String key = buildUploadStatusKey(uploadStatusDto.getFileMasterKey());
|
||||
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", ""));
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
public void saveUploadStatusFromTusHook(TusHookDto tusHookDto) {
|
||||
if (tusHookDto == null || tusHookDto.getFileMasterKey() == null || tusHookDto.getFileMasterKey().isBlank()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String key = buildUploadStatusKey(tusHookDto.getFileMasterKey().trim());
|
||||
String type = tusHookDto.getType() == null ? "" : tusHookDto.getType().trim().toLowerCase();
|
||||
long total = tusHookDto.getSize() == null ? 0L : tusHookDto.getSize();
|
||||
long uploaded = tusHookDto.getOffset() == null ? 0L : tusHookDto.getOffset();
|
||||
|
||||
String status = "UPLOADING";
|
||||
if ("post-finish".equals(type)) {
|
||||
status = "DONE";
|
||||
uploaded = total;
|
||||
} else if ("post-terminate".equals(type)) {
|
||||
status = "FAILED";
|
||||
} else if ("pre-create".equals(type)) {
|
||||
status = "PENDING";
|
||||
}
|
||||
|
||||
int percent = (total > 0) ? (int) Math.min(100, (uploaded * 100) / total) : 0;
|
||||
|
||||
HashOperations<String, Object, Object> ops = stringRedisTemplate.opsForHash();
|
||||
ops.put(key, "fileMasterKey", tusHookDto.getFileMasterKey());
|
||||
ops.put(key, "status", status);
|
||||
ops.put(key, "uploadedBytes", String.valueOf(uploaded));
|
||||
ops.put(key, "totalBytes", String.valueOf(total));
|
||||
ops.put(key, "percent", String.valueOf(percent));
|
||||
ops.put(key, "updatedAt", Instant.now().toString());
|
||||
|
||||
// 복원용으로 하루 유지
|
||||
stringRedisTemplate.expire(key, Duration.ofHours(24));
|
||||
}
|
||||
|
||||
public void insertFileAndSave(TusHookDto tusHookDto) {
|
||||
|
||||
}
|
||||
|
||||
private String buildUploadStatusKey(String fileMasterKey) {
|
||||
return "upload:alist:status:" + fileMasterKey;
|
||||
}
|
||||
|
||||
private long parseLong(Object value) {
|
||||
if (value == null) return 0L;
|
||||
try {
|
||||
return Long.parseLong(String.valueOf(value));
|
||||
} catch (Exception e) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
private String buildAuthCacheKey(String fileMasterKey, String uploadToken) {
|
||||
return "upload:alist:auth:" + fileMasterKey + ":" + sha256(uploadToken);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user