141 lines
5.1 KiB
Java
141 lines
5.1 KiB
Java
package net.gepafin.tendermanagement.util;
|
|
|
|
import com.amazonaws.services.s3.AmazonS3;
|
|
import com.amazonaws.services.s3.AmazonS3URI;
|
|
import com.amazonaws.services.s3.model.S3Object;
|
|
import net.gepafin.tendermanagement.model.request.AttachmentRequest;
|
|
import org.apache.poi.xwpf.usermodel.*;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import java.io.*;
|
|
import java.net.URI;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.Paths;
|
|
import java.util.*;
|
|
import java.util.Base64;
|
|
|
|
public class S3DocxProcessor {
|
|
|
|
private final AmazonS3 s3Client;
|
|
|
|
public static final Logger log = LoggerFactory.getLogger(S3DocxProcessor.class);
|
|
|
|
|
|
public S3DocxProcessor(AmazonS3 s3Client) {
|
|
this.s3Client = s3Client;
|
|
}
|
|
|
|
/**
|
|
* Fetches DOCX files from S3, replaces placeholders, and returns updated files
|
|
* as AttachmentRequest objects (ready for PEC API).
|
|
*
|
|
* @param s3Urls List of S3 file URLs
|
|
* @param replacements Map of placeholder -> value
|
|
* @return Map of original file name -> AttachmentRequest
|
|
* @throws IOException
|
|
*/
|
|
public Map<String, AttachmentRequest> processFiles(List<String> s3Urls,
|
|
Map<String, String> replacements) throws IOException {
|
|
Map<String, AttachmentRequest> processedFiles = new HashMap<>();
|
|
|
|
for (String s3Url : s3Urls) {
|
|
// Extract bucket & key from URL
|
|
AmazonS3URI s3Uri = new AmazonS3URI(s3Url);
|
|
String bucket = s3Uri.getBucket();
|
|
String key = s3Uri.getKey();
|
|
try (S3Object s3Object = s3Client.getObject(bucket, key);
|
|
InputStream originalStream = new BufferedInputStream(s3Object.getObjectContent())) {
|
|
byte[] updatedBytes=null;
|
|
if (isDocxFile(originalStream)) {
|
|
log.warn("Skipping non-DOCX file from S3: bucket={}, key={}", bucket, key);
|
|
updatedBytes = replacePlaceholders(originalStream, replacements);
|
|
}else {
|
|
// non-DOCX → just copy raw stream
|
|
updatedBytes = originalStream.readAllBytes();
|
|
log.info("Skipped placeholder replacement, copied file as-is: {}", key);
|
|
}
|
|
|
|
// convert to base64
|
|
String base64 = Base64.getEncoder().encodeToString(updatedBytes);
|
|
String fileString = "data:application/octet-stream;base64," + base64;
|
|
|
|
// create attachment request
|
|
AttachmentRequest attachment = new AttachmentRequest();
|
|
attachment.setName(extractFileName(key));
|
|
attachment.setFile(fileString);
|
|
|
|
processedFiles.put(key, attachment);
|
|
}
|
|
}
|
|
|
|
return processedFiles;
|
|
}
|
|
|
|
|
|
private byte[] replacePlaceholders(InputStream docInputStream,
|
|
Map<String, String> replacements) throws IOException {
|
|
try (XWPFDocument document = new XWPFDocument(docInputStream);
|
|
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
|
|
|
// replace in body paragraphs
|
|
replaceInParagraphs(document.getParagraphs(), replacements);
|
|
|
|
// replace inside tables
|
|
for (XWPFTable table : document.getTables()) {
|
|
for (XWPFTableRow row : table.getRows()) {
|
|
for (XWPFTableCell cell : row.getTableCells()) {
|
|
replaceInParagraphs(cell.getParagraphs(), replacements);
|
|
}
|
|
}
|
|
}
|
|
|
|
document.write(out);
|
|
return out.toByteArray();
|
|
}
|
|
}
|
|
|
|
private void replaceInParagraphs(List<XWPFParagraph> paragraphs, Map<String, String> replacements) {
|
|
for (XWPFParagraph paragraph : paragraphs) {
|
|
String paragraphText = paragraph.getText();
|
|
|
|
if (paragraphText != null && !paragraphText.isEmpty()) {
|
|
// Apply all replacements
|
|
for (Map.Entry<String, String> entry : replacements.entrySet()) {
|
|
paragraphText = paragraphText.replace(entry.getKey(), entry.getValue());
|
|
}
|
|
|
|
// Remove old runs
|
|
int runCount = paragraph.getRuns().size();
|
|
for (int i = runCount - 1; i >= 0; i--) {
|
|
paragraph.removeRun(i);
|
|
}
|
|
|
|
// Add one new run with replaced text
|
|
XWPFRun newRun = paragraph.createRun();
|
|
newRun.setText(paragraphText);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private String extractFileName(String key) {
|
|
int lastSlash = key.lastIndexOf('/');
|
|
if (lastSlash >= 0 && lastSlash < key.length() - 1) {
|
|
return key.substring(lastSlash + 1);
|
|
}
|
|
return key;
|
|
}
|
|
|
|
private boolean isDocxFile(InputStream inputStream) throws IOException {
|
|
inputStream.mark(4);
|
|
byte[] header = new byte[2];
|
|
int read = inputStream.read(header, 0, 2);
|
|
inputStream.reset();
|
|
|
|
// DOCX = ZIP = should start with PK (0x50 0x4B)
|
|
return read == 2 && header[0] == 0x50 && header[1] == 0x4B;
|
|
}
|
|
}
|