|
18 | 18 | import com.itextpdf.kernel.pdf.PdfDocument;
|
19 | 19 | import com.itextpdf.kernel.pdf.PdfReader;
|
20 | 20 | import com.itextpdf.kernel.pdf.PdfWriter;
|
| 21 | +import com.itextpdf.kernel.pdf.ReaderProperties; |
21 | 22 | import org.springframework.http.MediaType;
|
22 | 23 | import org.springframework.http.ResponseEntity;
|
23 | 24 | import org.springframework.transaction.annotation.Transactional;
|
|
32 | 33 | import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
33 | 34 |
|
34 | 35 | import java.io.ByteArrayInputStream;
|
| 36 | +import java.io.IOException; |
35 | 37 | import java.io.InputStream;
|
36 | 38 | import java.io.UnsupportedEncodingException;
|
37 | 39 | import java.net.URI;
|
@@ -144,14 +146,9 @@ public ScriptDetailResponseDTO getScriptDetailInfo(UserEntity userInfo, UUID pro
|
144 | 146 |
|
145 | 147 | }
|
146 | 148 |
|
147 |
| - public ResponseEntity<StreamingResponseBody> generateScriptPreview(UUID productId) { |
| 149 | + // 트랜잭션 없는 PDF 처리 메서드 |
| 150 | + public ResponseEntity<StreamingResponseBody> generateScriptPreview(String preSignedURL, int pagesToExtract) { |
148 | 151 | try {
|
149 |
| - final ProductEntity product = getProduct(productId); |
150 |
| - final String s3Key = product.getFilePath(); |
151 |
| - final String preSignedURL = s3Service.generatePreSignedURL(s3Key); |
152 |
| - |
153 |
| - int pagesToExtract = (product.getPlayType() == PlayType.LONG) ? 3 : 1; |
154 |
| - |
155 | 152 | // PDF 처리는 트랜잭션과 분리
|
156 | 153 | PdfExtractionResult result = processPreviewPdf(preSignedURL, pagesToExtract);
|
157 | 154 |
|
@@ -211,58 +208,71 @@ public PdfExtractionResult(int totalPageCount, byte[] extractedPdfBytes) {
|
211 | 208 | }
|
212 | 209 |
|
213 | 210 | // 트랜잭션과 분리된 PDF 처리 메서드
|
214 |
| - private PdfExtractionResult processPreviewPdf(String preSignedURL, int pagestoExtract) { |
215 |
| - try (InputStream fileStream = (new URI(preSignedURL).toURL().openStream())){ |
216 |
| - return extractPagesFromPdf(fileStream, pagestoExtract); |
| 211 | + private PdfExtractionResult processPreviewPdf(String preSignedURL, int pagesToExtract) { |
| 212 | + InputStream fileStream = null; |
| 213 | + try { |
| 214 | + fileStream = new URI(preSignedURL).toURL().openStream(); |
| 215 | + return extractPagesFromPdf(fileStream, pagesToExtract); |
217 | 216 | } catch (Exception e) {
|
218 | 217 | throw new RuntimeException("PDF 처리 실패", e);
|
| 218 | + } finally { |
| 219 | + if (fileStream != null) { |
| 220 | + try { |
| 221 | + fileStream.close(); |
| 222 | + } catch (IOException e) { |
| 223 | + log.error("InputStream 닫기 실패: {}", e.getMessage()); |
| 224 | + } |
| 225 | + } |
219 | 226 | }
|
220 | 227 | }
|
221 | 228 |
|
222 | 229 | // PDF의 특정 페이지까지 추출하는 함수
|
223 | 230 | private PdfExtractionResult extractPagesFromPdf(InputStream fileStream, int pagesToExtract) {
|
224 |
| - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { |
225 |
| - PdfReader reader = null; |
226 |
| - PdfWriter writer = null; |
227 |
| - PdfDocument originalDoc = null; |
228 |
| - PdfDocument newDoc = null; |
| 231 | + PdfReader reader = null; |
| 232 | + PdfWriter writer = null; |
| 233 | + PdfDocument originalDoc = null; |
| 234 | + PdfDocument newDoc = null; |
| 235 | + ByteArrayOutputStream outputStream = null; |
229 | 236 |
|
230 |
| - try { |
231 |
| - reader = new PdfReader(fileStream); |
232 |
| - reader.setMemorySavingMode(true); // 메모리 절약 모드 활성화 |
| 237 | + try { |
| 238 | + outputStream = new ByteArrayOutputStream(); |
233 | 239 |
|
234 |
| - writer = new PdfWriter(outputStream); |
| 240 | + // 안전 모드 설정 추가 |
| 241 | + ReaderProperties properties = new ReaderProperties(); |
| 242 | + reader = new PdfReader(fileStream, properties); |
| 243 | + reader.setMemorySavingMode(true); // 메모리 절약 모드 활성화 |
235 | 244 |
|
236 |
| - originalDoc = new PdfDocument(reader); |
237 |
| - newDoc = new PdfDocument(writer); |
| 245 | + writer = new PdfWriter(outputStream); |
238 | 246 |
|
239 |
| - final int totalPageCount = originalDoc.getNumberOfPages(); |
240 |
| - final int endPage = Math.min(pagesToExtract, totalPageCount); |
| 247 | + originalDoc = new PdfDocument(reader); |
| 248 | + newDoc = new PdfDocument(writer); |
241 | 249 |
|
242 |
| - originalDoc.copyPagesTo(1, endPage, newDoc); |
| 250 | + final int totalPageCount = originalDoc.getNumberOfPages(); |
| 251 | + final int endPage = Math.min(pagesToExtract, totalPageCount); |
243 | 252 |
|
244 |
| - // 명시적으로 문서 닫기 (역순으로) |
245 |
| - newDoc.close(); |
246 |
| - originalDoc.close(); |
247 |
| - writer.close(); |
248 |
| - reader.close(); |
| 253 | + originalDoc.copyPagesTo(1, endPage, newDoc); |
249 | 254 |
|
250 |
| - return new PdfExtractionResult(totalPageCount, outputStream.toByteArray()); |
251 |
| - } catch (Exception e) { |
252 |
| - // 예외 발생 시에도 리소스 해제 보장 (역순으로) |
253 |
| - closeQuietly(newDoc, "newDoc"); |
254 |
| - closeQuietly(originalDoc, "originalDoc"); |
255 |
| - closeQuietly(writer, "writer"); |
256 |
| - closeQuietly(reader, "reader"); |
| 255 | + // 명시적으로 문서 닫기 (역순으로) |
| 256 | + newDoc.close(); |
| 257 | + originalDoc.close(); |
| 258 | + writer.close(); |
| 259 | + reader.close(); |
257 | 260 |
|
258 |
| - throw new RuntimeException("PDF 페이지 추출 실패", e); |
259 |
| - } |
| 261 | + return new PdfExtractionResult(totalPageCount, outputStream.toByteArray()); |
260 | 262 | } catch (Exception e) {
|
261 |
| - log.error("PDF 처리 중 오류 발생: error={}", e.getMessage()); |
| 263 | + closeAllResources(newDoc, originalDoc, writer, reader, outputStream); |
262 | 264 | throw new RuntimeException("PDF 처리 실패", e);
|
263 | 265 | }
|
264 | 266 | }
|
265 | 267 |
|
| 268 | + private void closeAllResources(PdfDocument newDoc, PdfDocument originalDoc, PdfWriter writer, PdfReader reader, ByteArrayOutputStream outputStream) { |
| 269 | + closeQuietly(newDoc, "newDoc"); |
| 270 | + closeQuietly(originalDoc, "originalDoc"); |
| 271 | + closeQuietly(writer, "writer"); |
| 272 | + closeQuietly(reader, "reader"); |
| 273 | + closeQuietly(outputStream, "outputStream"); |
| 274 | + } |
| 275 | + |
266 | 276 | // 리소스를 안전하게 닫는 유틸리티 메서드
|
267 | 277 | private void closeQuietly(AutoCloseable resource, String resourceName) {
|
268 | 278 | if (resource != null) {
|
|
0 commit comments