|
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