This commit is contained in:
Anthony Stirling 2025-07-10 14:34:43 +01:00
parent 98b4763c96
commit f19ff6691d
2 changed files with 31 additions and 7 deletions

View File

@ -1,6 +1,7 @@
package stirling.software.common.service; package stirling.software.common.service;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
@ -358,7 +359,15 @@ public class TaskManager {
ZipEntry entry; ZipEntry entry;
while ((entry = zipIn.getNextEntry()) != null) { while ((entry = zipIn.getNextEntry()) != null) {
if (!entry.isDirectory()) { if (!entry.isDirectory()) {
byte[] fileContent = zipIn.readAllBytes(); // Use buffered reading for memory safety
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = zipIn.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
byte[] fileContent = out.toByteArray();
String contentType = determineContentType(entry.getName()); String contentType = determineContentType(entry.getName());
String individualFileId = fileStorage.storeBytes(fileContent, entry.getName()); String individualFileId = fileStorage.storeBytes(fileContent, entry.getName());

View File

@ -1,5 +1,7 @@
package stirling.software.common.controller; package stirling.software.common.controller;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -99,9 +101,7 @@ public class JobController {
byte[] fileContent = fileStorage.retrieveBytes(singleFile.getFileId()); byte[] fileContent = fileStorage.retrieveBytes(singleFile.getFileId());
return ResponseEntity.ok() return ResponseEntity.ok()
.header("Content-Type", singleFile.getContentType()) .header("Content-Type", singleFile.getContentType())
.header( .header("Content-Disposition", createContentDispositionHeader(singleFile.getFileName()))
"Content-Disposition",
"attachment; filename=\"" + singleFile.getFileName() + "\"")
.body(fileContent); .body(fileContent);
} catch (Exception e) { } catch (Exception e) {
log.error("Error retrieving file for job {}: {}", jobId, e.getMessage(), e); log.error("Error retrieving file for job {}: {}", jobId, e.getMessage(), e);
@ -276,9 +276,7 @@ public class JobController {
return ResponseEntity.ok() return ResponseEntity.ok()
.header("Content-Type", contentType) .header("Content-Type", contentType)
.header( .header("Content-Disposition", createContentDispositionHeader(fileName))
"Content-Disposition",
"attachment; filename=\"" + fileName + "\"")
.body(fileContent); .body(fileContent);
} catch (Exception e) { } catch (Exception e) {
log.error("Error retrieving file {}: {}", fileId, e.getMessage(), e); log.error("Error retrieving file {}: {}", fileId, e.getMessage(), e);
@ -301,4 +299,21 @@ public class JobController {
// TODO: Consider adding a fileId -> ResultFile mapping in TaskManager // TODO: Consider adding a fileId -> ResultFile mapping in TaskManager
return taskManager.findResultFileByFileId(fileId); return taskManager.findResultFileByFileId(fileId);
} }
/**
* Create Content-Disposition header with UTF-8 filename support
*
* @param fileName The filename to encode
* @return Content-Disposition header value
*/
private String createContentDispositionHeader(String fileName) {
try {
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8)
.replace("+", "%20"); // URLEncoder uses + for spaces, but we want %20
return "attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + encodedFileName;
} catch (Exception e) {
// Fallback to basic filename if encoding fails
return "attachment; filename=\"" + fileName + "\"";
}
}
} }