Compress fixes

This commit is contained in:
Anthony Stirling 2025-03-19 00:01:41 +00:00
parent 4194efed90
commit 0fb76a1f4c
3 changed files with 49 additions and 21 deletions

View File

@ -48,6 +48,22 @@ public class EndpointConfiguration {
return endpointStatuses.getOrDefault(endpoint, true);
}
public boolean isGroupEnabled(String group) {
Set<String> endpoints = endpointGroups.get(group);
if (endpoints == null || endpoints.isEmpty()) {
log.debug("Group '{}' does not exist or has no endpoints", group);
return false;
}
for (String endpoint : endpoints) {
if (!isEndpointEnabled(endpoint)) {
return false;
}
}
return true;
}
public void addEndpointToGroup(String group, String endpoint) {
endpointGroups.computeIfAbsent(group, k -> new HashSet<>()).add(endpoint);
}

View File

@ -7,6 +7,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@ -46,7 +47,7 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.config.EndpointConfiguration;
import stirling.software.SPDF.model.api.misc.OptimizePdfRequest;
import stirling.software.SPDF.service.CustomPDFDocumentFactory;
import stirling.software.SPDF.utils.GeneralUtils;
@ -62,12 +63,14 @@ import stirling.software.SPDF.utils.WebResponseUtils;
public class CompressController {
private final CustomPDFDocumentFactory pdfDocumentFactory;
@Autowired
public CompressController(CustomPDFDocumentFactory pdfDocumentFactory) {
private final boolean qpdfEnabled;
public CompressController(CustomPDFDocumentFactory pdfDocumentFactory, EndpointConfiguration endpointConfiguration) {
this.pdfDocumentFactory = pdfDocumentFactory;
this.qpdfEnabled = endpointConfiguration.isGroupEnabled("qpdf");
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ -478,17 +481,17 @@ public class CompressController {
}
// Create initial input file
Path originalFile = Files.createTempFile("input_", ".pdf");
Path originalFile = Files.createTempFile("original_", ".pdf");
inputFile.transferTo(originalFile.toFile());
long inputFileSize = Files.size(originalFile);
// Start with original as current working file
Path currentFile = originalFile;
Path currentFile = Files.createTempFile("working_", ".pdf");
Files.copy(originalFile, currentFile, StandardCopyOption.REPLACE_EXISTING);
// Keep track of all temporary files for cleanup
List<Path> tempFiles = new ArrayList<>();
tempFiles.add(originalFile);
tempFiles.add(currentFile);
try {
if (autoMode) {
double sizeReductionRatio = expectedOutputSize / (double) inputFileSize;
@ -498,7 +501,10 @@ public class CompressController {
boolean sizeMet = false;
boolean imageCompressionApplied = false;
boolean qpdfCompressionApplied = false;
if(qpdfEnabled && optimizeLevel <= 3) {
optimizeLevel = 4;
}
while (!sizeMet && optimizeLevel <= 9) {
// Apply image compression for levels 4-9
if ((optimizeLevel >= 4 || Boolean.TRUE.equals(convertToGrayscale))
@ -520,7 +526,7 @@ public class CompressController {
}
// Apply QPDF compression for all levels
if (!qpdfCompressionApplied) {
if (!qpdfCompressionApplied && qpdfEnabled) {
long preQpdfSize = Files.size(currentFile);
log.info("Pre-QPDF file size: {}", GeneralUtils.formatBytes(preQpdfSize));
@ -572,6 +578,12 @@ public class CompressController {
// If QPDF fails, keep using the current file
log.warn("QPDF compression failed, continuing with current file");
}
} else if (!qpdfCompressionApplied) {
// If QPDF is disabled, mark as applied and log
if (!qpdfEnabled) {
log.info("Skipping QPDF compression as QPDF group is disabled");
}
qpdfCompressionApplied = true;
}
// Check if file size is within expected size or not auto mode

View File

@ -77,7 +77,7 @@ public class CustomPDFDocumentFactory {
}
long fileSize = file.length();
log.info("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024));
log.debug("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024));
return loadAdaptively(file, fileSize);
}
@ -92,7 +92,7 @@ public class CustomPDFDocumentFactory {
}
long fileSize = Files.size(path);
log.info("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024));
log.debug("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024));
return loadAdaptively(path.toFile(), fileSize);
}
@ -104,7 +104,7 @@ public class CustomPDFDocumentFactory {
}
long dataSize = input.length;
log.info("Loading PDF from byte array, size: {}MB", dataSize / (1024 * 1024));
log.debug("Loading PDF from byte array, size: {}MB", dataSize / (1024 * 1024));
return loadAdaptively(input, dataSize);
}
@ -150,7 +150,7 @@ public class CustomPDFDocumentFactory {
long actualFreeMemory = maxMemory - usedMemory;
// Log memory status
log.info(
log.debug(
"Memory status - Free: {}MB ({}%), Used: {}MB, Max: {}MB",
actualFreeMemory / (1024 * 1024),
String.format("%.2f", freeMemoryPercent),
@ -160,21 +160,21 @@ public class CustomPDFDocumentFactory {
// If free memory is critically low, always use file-based caching
if (freeMemoryPercent < MIN_FREE_MEMORY_PERCENTAGE
|| actualFreeMemory < MIN_FREE_MEMORY_BYTES) {
log.info(
log.debug(
"Low memory detected ({}%), forcing file-based cache",
String.format("%.2f", freeMemoryPercent));
return createScratchFileCacheFunction(MemoryUsageSetting.setupTempFileOnly());
} else if (contentSize < SMALL_FILE_THRESHOLD) {
log.info("Using memory-only cache for small document ({}KB)", contentSize / 1024);
log.debug("Using memory-only cache for small document ({}KB)", contentSize / 1024);
return IOUtils.createMemoryOnlyStreamCache();
} else if (contentSize < LARGE_FILE_THRESHOLD) {
// For medium files (10-50MB), use a mixed approach
log.info(
log.debug(
"Using mixed memory/file cache for medium document ({}MB)",
contentSize / (1024 * 1024));
return createScratchFileCacheFunction(MemoryUsageSetting.setupMixed(LARGE_FILE_USAGE));
} else {
log.info("Using file-based cache for large document");
log.debug("Using file-based cache for large document");
return createScratchFileCacheFunction(MemoryUsageSetting.setupTempFileOnly());
}
}
@ -237,7 +237,7 @@ public class CustomPDFDocumentFactory {
byte[] bytes, long size, StreamCacheCreateFunction cache, String password)
throws IOException {
if (size >= SMALL_FILE_THRESHOLD) {
log.info("Writing large byte array to temp file for password-protected PDF");
log.debug("Writing large byte array to temp file for password-protected PDF");
Path tempFile = createTempFile("pdf-bytes-");
Files.write(tempFile, bytes);
@ -270,7 +270,7 @@ public class CustomPDFDocumentFactory {
private PDDocument loadFromBytes(byte[] bytes, long size, StreamCacheCreateFunction cache)
throws IOException {
if (size >= SMALL_FILE_THRESHOLD) {
log.info("Writing large byte array to temp file");
log.debug("Writing large byte array to temp file");
Path tempFile = createTempFile("pdf-bytes-");
Files.write(tempFile, bytes);
@ -318,7 +318,7 @@ public class CustomPDFDocumentFactory {
// Temp file handling with enhanced logging
private Path createTempFile(String prefix) throws IOException {
Path file = Files.createTempFile(prefix + tempCounter.incrementAndGet() + "-", ".tmp");
log.info("Created temp file: {}", file);
log.debug("Created temp file: {}", file);
return file;
}