v2.0 changes
This commit is contained in:
@@ -208,14 +208,14 @@ upload_file_in_chunks() {
|
|||||||
if [ "$DEBUG_MODE" = "true" ]; then
|
if [ "$DEBUG_MODE" = "true" ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo "DEBUG: SESSION_ID='$SESSION_ID'"
|
echo "DEBUG: SESSION_ID='$SESSION_ID'"
|
||||||
echo "DEBUG: Uploading to: $API_BASE/upload/chunk?uploadSessionId=$SESSION_ID&fileType=$file_type&chunkIndex=$chunk_index&totalChunks=$total_chunks&fileName=$file_name"
|
echo "DEBUG: Uploading chunk to: $API_BASE/upload/chunk?uploadSessionId=$SESSION_ID&fileType=$file_type&chunkIndex=$chunk_index&totalChunks=$total_chunks&fileName=$file_name"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Send metadata as query params (for Java proxy to capture) and only file as multipart
|
# Send metadata as query params and chunk file as raw binary body
|
||||||
# Add explicit headers that nginx might require
|
# This works with Java proxy that forwards body as-is
|
||||||
RESPONSE=$(curl -s -X POST "$API_BASE/upload/chunk?uploadSessionId=$SESSION_ID&fileType=$file_type&chunkIndex=$chunk_index&totalChunks=$total_chunks&fileName=$file_name" \
|
RESPONSE=$(curl -s -X POST "$API_BASE/upload/chunk?uploadSessionId=$SESSION_ID&fileType=$file_type&chunkIndex=$chunk_index&totalChunks=$total_chunks&fileName=$file_name" \
|
||||||
-H "X-Forwarded-For: 127.0.0.1" \
|
-H "Content-Type: application/octet-stream" \
|
||||||
-F "chunk=@$chunk_file")
|
--data-binary "@$chunk_file")
|
||||||
|
|
||||||
if [ "$DEBUG_MODE" = "true" ]; then
|
if [ "$DEBUG_MODE" = "true" ]; then
|
||||||
echo "DEBUG: Chunk response: $RESPONSE"
|
echo "DEBUG: Chunk response: $RESPONSE"
|
||||||
|
|||||||
@@ -182,6 +182,39 @@ public class CfDeployController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/upload/chunk", consumes = "application/octet-stream")
|
||||||
|
public ResponseEntity<ChunkUploadResponse> uploadChunkRaw(
|
||||||
|
@RequestParam("uploadSessionId") String uploadSessionId,
|
||||||
|
@RequestParam("fileType") String fileType,
|
||||||
|
@RequestParam("chunkIndex") Integer chunkIndex,
|
||||||
|
@RequestParam("totalChunks") Integer totalChunks,
|
||||||
|
@RequestParam(value = "fileName", required = false) String fileName,
|
||||||
|
@RequestBody byte[] chunkData) {
|
||||||
|
try {
|
||||||
|
log.debug("Receiving raw chunk {}/{} for session: {}, fileType: {} ({} bytes)",
|
||||||
|
chunkIndex + 1, totalChunks, uploadSessionId, fileType, chunkData.length);
|
||||||
|
|
||||||
|
// Validate file type
|
||||||
|
if (!fileType.equals("jarFile") && !fileType.equals("manifest")) {
|
||||||
|
throw new IllegalArgumentException("Invalid file type. Must be 'jarFile' or 'manifest'");
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkedUploadService.uploadChunkRaw(uploadSessionId, fileType, fileName,
|
||||||
|
chunkIndex, totalChunks, chunkData);
|
||||||
|
|
||||||
|
var session = chunkedUploadService.getSession(uploadSessionId);
|
||||||
|
var fileState = session.getFileStates().get(fileType);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(ChunkUploadResponse.success(
|
||||||
|
uploadSessionId, fileType, chunkIndex, totalChunks,
|
||||||
|
fileState.getReceivedChunkCount()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error uploading raw chunk", e);
|
||||||
|
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||||
|
.body(ChunkUploadResponse.failure("Failed to upload chunk: " + e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/upload/finalize")
|
@PostMapping("/upload/finalize")
|
||||||
public ResponseEntity<DeploymentStatus> finalizeUpload(
|
public ResponseEntity<DeploymentStatus> finalizeUpload(
|
||||||
@RequestParam("uploadSessionId") String uploadSessionId,
|
@RequestParam("uploadSessionId") String uploadSessionId,
|
||||||
|
|||||||
@@ -101,6 +101,62 @@ public class ChunkedUploadService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void uploadChunkRaw(String sessionId, String fileType, String fileName,
|
||||||
|
int chunkIndex, int totalChunks, byte[] chunkData) throws IOException {
|
||||||
|
UploadSession session = activeSessions.get(sessionId);
|
||||||
|
if (session == null) {
|
||||||
|
throw new IllegalArgumentException("Upload session not found or expired: " + sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
session.updateLastAccessed();
|
||||||
|
|
||||||
|
// Get or create file upload state
|
||||||
|
UploadSession.FileUploadState fileState = session.getFileStates()
|
||||||
|
.computeIfAbsent(fileType, k -> {
|
||||||
|
String targetFileName = fileType.equals("manifest") ? "manifest.yml" : fileName;
|
||||||
|
Path targetPath = session.getWorkingDirectory().resolve(targetFileName);
|
||||||
|
return new UploadSession.FileUploadState(fileName, totalChunks, targetPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Validate total chunks consistency
|
||||||
|
if (fileState.getTotalChunks() != totalChunks) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format("Total chunks mismatch for %s: expected %d, got %d",
|
||||||
|
fileType, fileState.getTotalChunks(), totalChunks));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write chunk to file using sequential append mode
|
||||||
|
// This supports variable chunk sizes - chunks MUST be uploaded in order (0, 1, 2, ...)
|
||||||
|
Path targetPath = fileState.getTargetPath();
|
||||||
|
|
||||||
|
// Verify chunks are uploaded in order
|
||||||
|
if (chunkIndex != fileState.getReceivedChunkCount()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format("Chunks must be uploaded in order. Expected chunk %d but received %d",
|
||||||
|
fileState.getReceivedChunkCount(), chunkIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
try (var outputStream = Files.newOutputStream(targetPath,
|
||||||
|
java.nio.file.StandardOpenOption.CREATE,
|
||||||
|
java.nio.file.StandardOpenOption.APPEND)) {
|
||||||
|
|
||||||
|
// Write raw byte array directly
|
||||||
|
outputStream.write(chunkData);
|
||||||
|
|
||||||
|
log.debug("Appended raw chunk {} ({} bytes) to {}",
|
||||||
|
chunkIndex, chunkData.length, targetPath.getFileName());
|
||||||
|
}
|
||||||
|
|
||||||
|
fileState.markChunkReceived(chunkIndex);
|
||||||
|
log.info("Session {}: Received raw chunk {}/{} for {} ({} bytes)",
|
||||||
|
sessionId, chunkIndex + 1, totalChunks, fileType, chunkData.length);
|
||||||
|
|
||||||
|
if (fileState.isComplete()) {
|
||||||
|
log.info("Session {}: File {} upload completed ({} chunks)",
|
||||||
|
sessionId, fileType, totalChunks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public UploadSession getSession(String sessionId) {
|
public UploadSession getSession(String sessionId) {
|
||||||
UploadSession session = activeSessions.get(sessionId);
|
UploadSession session = activeSessions.get(sessionId);
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user