iTextPDF 버전변경 및 확장된 기능(JPEG 변환, 압축, 다운로드)

Daniel·2024년 3월 19일
0

Back-End

목록 보기
34/42

들어가며

전에 iTextPDF와 Commons-Net으로 PDF 생성 및 FTP 서버 업로드 기능 구현하기 포스트를 통해 서버로 들어 온 사용자 정보를 이용해 pdf 파일을 생성 후 외부 FTP 서버로 업로드 시키는 기능을 구현해보았습니다.
요구사항의 변화로 많은 부분 수정이 있었습니다. 어떻게 수정되었는지 포스트를 작성해 기록해보려 합니다.

1. iTextPDF 와 Connons-Net 라이브러리를 이용해 PDF파일생성 및 FTP 서버 업로드 기능 구현

  • 기존에 구현했던 기능입니다.

2. 서버에서 생성된 PDF 파일 JPEG로 변환 후 압축해서 업로드하기

  • 파일의 용량이 너무커서 이미지 파일로 변환 및 압축해서 업로드 요청이 들어와 수정 된 기능입니다.
    (파일의 크기는 그렇게 크지 않았지만...더 줄여달라는..? 아무래도 모든 사용자에 관한 파일을 저장해야하다보니...무튼 시간이 좀 걸렸습니다ㅠ)

3. iTextPDF 라이브러리 버전 변경

  1. 기능을 위해 버전을 변경했습니다. (필자가 쓰던 버전은 알고보니 옛버전 ㅠ)

4. 서버에서 생성된 파일 APPLICATION_PDF 형식으로 내려주기

  • 추가적으로 클라이언트쪽에 서버에서 생성된 파일을 내려주는 기능까지...
  1. 기능은 이전 포스트에서 작성했으므로, 현재 이 포스트에서는 3, 2, 4 순으로 작성해 보겠습니다.

사용기술

  • iTextPDF (5.5.13.3 -> 7.2.4)
    텍스트나 html 등의 문서를 pdf로 만들어주는 자바 라이브러리

  • Commons-Net (3.9.0)
    다양한 프로토콜(FTP, TFTP, SMTP, ...)에 대한 지원을 할 수 있도록 도와주는 자바 라이브러리
    클라이언트 모듈을 제공합니다.

  • PDFBox(2.0.30)
    서버에서 생성된 pdf 파일을 jpeg 로 변환 할 때 사용했습니다.

  • JAVA 1.8

  • Gradle 4.10.3

  • SpringBoot 2.1.5

적용예시

1. iTextPDF 라이브러리 버전 변경

조금 더 세밀한 설정과 레퍼런스가 많은 7.x.x 버전으로 변경하기로 했습니다.

// 변경 전
compile 'com.itextpdf:itextpdf:5.5.13.3'

// 변경 후
compile 'com.itextpdf:itext7-core:7.2.4'

2. 서버에서 생성된 PDF 파일 JPEG로 변환 후 압축해서 업로드하기

pdf파일을 생성하는 메서드에서 뭔가 바꿔주면 되지않을까..? 라고 생각했습니다.

먼저 이 전 포스트의 pdf파일을 생성하는 메서드를 확인해보겠습니다.

public byte[] createPdf(Map<String, Object> data) throws IOException, DocumentException {
	
	try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {   // ByteArrayOutputStream -> AutoCloseable 하므로 try-with-resources 사용
		
		// PDF 파일 자체를 나타내는 Document 객체 초기화
	    Document document = new Document(PageSize.A4, 5, 5, 10, 10);  
	    
	    // Document 객체와 ByteArrayOutputStream 을 연결하여 PDF 파일을 메모리에서 생성하도록 함
	    PdfWriter.getInstance(document, out);  
	    
	    // Document 객체를 열고 작성 후 닫음
	    document.open();  
	    document.add(contractMaker(data));  
	    document.close();  
	    
	    // PDF 파일을 FTP 서버로 보낼 수 있도록 PDF 파일의 내용이 저장된 바이트 배열 반환
	    return out.toByteArray();  
	}
}

버전이 바뀜에 따라 메서드와 사용법등이 좀 바뀌었습니다.

  • 버전 변경 후
public byte[] createPdf(Map<String, Object> data) throws IOException, DocumentException {
	
	try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
		
		PdfWriter pdfWriter = new PdfWriter(out);
		Document document = new Document(new PdfDocument(pdfWriter), PageSize.A4);
		
		document.add(contractMaker(data));
		document.close();
		
		return out.toByteArray();
	}
}

자, 이제 변경된 버전은 반영했습니다.

위에서 생성된 pdf파일을 jpeg로 변환 후 압축 해서 내려줘야 하는데...
구글링을 해보니 pdfbox 라이브러리의 메서드를 사용하면 된다고해서 사용해 보았습니다.

  • pdf 파일을 jpeg 이미지로 변환 & 압축
compile 'org.apache.pdfbox:pdfbox:2.0.30'
private byte[] pdfTOjpeg(byte[] bytes) throws IOException {  
	
	// .load() 를 사용해 파라미터로 들어온 pdf 파일을 메모리에 로드 시켜줍니다.
    try (PDDocument pdDocument = PDDocument.load(bytes);  
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {  
        
        // pdf의 각 페이지를 이미지로 렌더링
	    PDFRenderer renderer = new PDFRenderer(pdDocument);  
	    
	    for (int i = 0; i < pdDocument.getNumberOfPages(); i++) {  
	        BufferedImage image = renderer.renderImageWithDPI(i, 300);  
	        float quality = 0.5f;  
	        
	        // 이미지크기는 pdf 파일의 크기(가로x세로) / 2
	        // Graphics2D 객체를 사용해 크기가 조절된 이미지를 그린다.
	        int width = image.getWidth() / 2;  
	        int height = image.getHeight() / 2;  
	        BufferedImage resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);  
	        Graphics2D g = resizedImage.createGraphics();  
	        g.drawImage(image, 0, 0, width, height, null);  
	        g.dispose();  
	        
	         
	          try ( ImageOutputStream ios = ImageIO.createImageOutputStream(outputStream); ) {  
	             ImageWriter writer = obtainImageWriter("JPEG");  
	             writer.setOutput(ios);  
	             ImageWriteParam param = writer.getDefaultWriteParam();  
	             
	             // 압축방식(모드) 설정
	             param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);  // 개발자가 압축 품질 설정한다는 모드
	             // 압축(0과 1사이의 값으로 설정)
	             param.setCompressionQuality(quality);  
	             
	             writer.write(null, new IIOImage(resizedImage, null, null), param);  
	          }  
	       }  
	       
       return outputStream.toByteArray();  
    }  
}

위 메서드에서 반환되는 byte[] 는 변환 후 압축된 jpeg 파일입니다.

3. 서버에서 생성된 파일 APPLICATION_PDF 형식으로 내려주기

자, 이제 내려줄 일만 남았습니다.

요구 사항 변경 : pdf 파일로 압축해서 저장 후 내려주세요.

예예...다 됩니다....

  • pdf 파일 압축
public byte[] createPdf(Map<String, Object> data) throws IOException {  
  
    try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {  
	    PdfWriter pdfWriter = new PdfWriter(out);  
	    
	    // pdf 파일 압축 수준 설정 (1~9)
	    pdfWriter.setCompressionLevel(BEST_COMPRESSION);  // 9
	    
	    Document document = new Document(new PdfDocument(pdfWriter), PageSize.A4);  
	       
	    document.add(contractMaker(data));  
	    document.close();  
		
	    return out.toByteArray();  
    }
}

요구 사항도 반영했으니, 내려주도록 해볼까요?

  • Controller.java
// * 계약서를 생성 후 이용기관 FPT 서버에 저장 후 조회해서 내려줌  
@PostMapping("/contract")  
public ResponseEntity<Object> contract(@RequestBody Map<String, Object> body) throws  IOException,  IllegalAccessException {  
	  
    byte[] pdfData = contractSaveService.saveEndFindViewer(body);  
	  
    if (pdfData != null) {  
	    ByteArrayResource resource = new ByteArrayResource(pdfData);  
	  
       return ResponseEntity.ok()  
          // .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"contract.pdf\"") // 다운로드  
          .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"contract.pdf\"") // 바로보기  
          .contentType(MediaType.APPLICATION_PDF)  
          .contentLength(pdfData.length)  
          .body(resource);  
    }  
	  
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body("파일을 찾지 못했습니다.");  
}
  • Service.java
public byte[] saveEndFindViewer(Map<String, Object> body) throws IOException, IllegalAccessException {  
    // 파일명을 정해서 넘겨줘야함 (yyyyMMdd_고객번호)  
    String fileName = body.get("contractDate") + "_" + body.get("clientCode");  
	  
    // ftpFileSender.upload(fileName + ".jpeg", pdfMaker.createPdf(body));  
    return ftpFileSender.upload(fileName + ".pdf", pdfMaker.createPdf(body)) ?  
	    ftpFileSender.download(fileName + ".pdf") : null;  
}

3-1. MediaType.APPLICATION_PDF

MediaType.APPLICATION_PDF는 Spring Framework에서 사용되는 상수로, HTTP 헤더의 'Content-Type'을 'application/pdf'로 설정하는 데 사용됩니다.
이는 주로 PDF 파일을 HTTP 응답으로 전송할 때 사용되며, 클라이언트에게 응답 본문이 PDF 형식임을 명시적으로 알립니다.
이를 통해 웹 애플리케이션은 PDF 문서를 브라우저나 다른 클라이언트에 효율적으로 제공할 수 있습니다.

응답헤더에 응답값의 형식(APPLICATION_PDF)을 명시해 준 후 추가로 파일을 다운로드하려면 Content-Disposition 헤더에 attachment; filename="filename.pdf"를, 바로 보여주려면 inline; filename="filename.pdf"를 설정합니다. 이를 통해 브라우저가 파일을 어떻게 처리할지 결정하게 됩니다. attachment는 파일 다운로드를, inline은 브라우저 내에서 파일을 바로 보여주는 데 사용됩니다.

막혔던 부분

jpeg 변환과 압축 부분에서 레퍼런스가 많지 않아 조금 막혔습니다.
좋은 경험이였습니다!

profile
응애 나 애기 개발자

0개의 댓글