Page tree
Skip to end of metadata
Go to start of metadata

1. 이미지, 동영상, 파일 업로드

주의사항

가이드로 제공되는 아래 코드 중 파일 업로드 부분은 샘플 코드로서 보안 관련 처리가 미흡합니다.

파일 업로드 부분은 프로젝트 내부에서 사용하시는 부분을 그대로 사용하시고 아래 코드를 참고하셔서 연동 부분을 처리해주세요. 

upload
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.*;


@Controller
public class UploadController {
    static String IMAGE_UPLOAD_DIR_REL_PATH = "uploads";
   
    @RequestMapping(value = "/uploadFile.do", method = RequestMethod.POST)
	@ResponseBody
	public Map<String, Object> uploadFile(HttpServletRequest request, @RequestParam("file") MultipartFile file)
	        throws IOException {
	    String ROOT_ABS_PATH = request.getSession().getServletContext().getRealPath("");
	    String UPLOAD_DIR_ABS_PATH = ROOT_ABS_PATH + File.separator + IMAGE_UPLOAD_DIR_REL_PATH;
	
	    makeDirectory(UPLOAD_DIR_ABS_PATH);
	
	    String fileName = file.getOriginalFilename();
	    String ext = "";
		String contentType = file.getContentType();
		if(contentType != null) {
		    ext = "." + contentType.substring(contentType.lastIndexOf('/') + 1);
		} else if (fileName.lastIndexOf('.') > 0) {
		    ext = fileName.substring(fileName.lastIndexOf('.'));
		}
		if (ext.indexOf(".jpeg") > -1) { // jpg가 더많이쓰여서 jpeg는 jpg로 변환
		    ext = ".jpg";
		}
	    String saveFileName = UUID.randomUUID().toString() + ext;
	    String saveFileAbsPath = UPLOAD_DIR_ABS_PATH + File.separator + saveFileName;
	
	    writeFile(saveFileAbsPath, file.getBytes());
	
	    Map<String, Object> map = new HashMap<String, Object>();
	
	    // 브라우저에서 접근가능한 경로를 uploadPath에 담아서 넘겨줍니다.
	    map.put("uploadPath", "uploads/" + saveFileName);
	
	    return map;
	}

	/**
	 * 파일을 씁니다.
	 */
	private static void writeFile(String path, byte[] bytes) throws IOException {
	    OutputStream os = null;
	    try {
	        os = new FileOutputStream(path);
	        os.write(bytes);
	    } finally {
	        if (os != null) os.close();
	    }
	}
	
	/**
	 * 디렉토리가 없는 경우 디렉토리를 생성합니다.
	 */
	private static void makeDirectory(String dirPath) {
	    File dir = new File(dirPath);
	    if (!dir.exists()) {
	        dir.mkdir();
	    }
	}
}

2. HWP, MS 워드, 엑셀 문서 임포트

주의사항

가이드로 제공되는 아래 코드 중 파일 업로드 부분은 샘플 코드로서 보안 관련 처리가 미흡합니다.

파일 업로드 부분은 프로젝트 내부에서 사용하시는 부분을 그대로 사용하시고 아래 코드를 참고하셔서 연동 부분을 처리해주세요. 

import
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.*;
import java.util.zip.InflaterInputStream;

@Controller
public class ImportController {
    static String DOC_UPLOAD_DIR_REL_PATH = "uploads" + File.separator + "docs";
	static String OUTPUT_DIR_REL_PATH = "uploads" + File.separator + "output";

    @RequestMapping(value = "/importDoc.do", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> importDoc(HttpServletRequest request, @RequestParam("file") MultipartFile importFile)
	        throws IOException {
	    String ROOT_ABS_PATH = request.getSession().getServletContext().getRealPath("");
	    String UPLOAD_DIR_ABS_PATH = ROOT_ABS_PATH + File.separator + DOC_UPLOAD_DIR_REL_PATH;
	
	    makeDirectory(UPLOAD_DIR_ABS_PATH);
	
	    String fileName = importFile.getOriginalFilename();
	    String inputFileAbsPath = UPLOAD_DIR_ABS_PATH + File.separator + fileName;
	
	    writeFile(inputFileAbsPath, importFile.getBytes());
	
	    // 파일별로 변환결과를 저장할 경로 생성
	    Calendar cal = Calendar.getInstance();
	    String yearMonth = String.format("%04d%02d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1);
	    String uuid = UUID.randomUUID().toString();
	    String worksDirAbsPath = ROOT_ABS_PATH + File.separator + OUTPUT_DIR_REL_PATH + File.separator + yearMonth + File.separator + uuid;

	    makeDirectory(worksDirAbsPath);

	    // 문서 변환
	    executeConverter(inputFileAbsPath, worksDirAbsPath);
	
	    // 변환이 끝난 원본파일은 삭제한다.
	    deleteFile(inputFileAbsPath);
	
	    // 변환된 pb파일을 읽어서 serialzie
	    // v2.3.0 부터 파일명이 document.word.pb에서 document.pb로 변경됨
	    String pbAbsPath = worksDirAbsPath + File.separator + "document.pb";
	    Integer[] serializedData = serializePbData(pbAbsPath);
	
	    // pb파일은 삭제
	    // v2.3.0 부터 파일명이 document.word.pb에서 document.pb로 변경됨
	    deleteFile(pbAbsPath);
	
	    Map<String, Object> map = new HashMap<String, Object>();
	    map.put("serializedData", serializedData);
	    // 브라우저에서 접근가능한 경로를 importPath에 담아서 넘겨줍니다.
	    // OUTPUT_DIR_REL_PATH 경로에 맞춰서 수정해야 합니다.
	    map.put("importPath", "uploads/output/" + yearMonth + "/" + uuid);
	
	    return map;
	}
	
	/**
	 * 문서 변환 모듈을 실행합니다.
	 */
	public static int executeConverter(String inputFilePath, String outputFilePath) {
	    String SEDOC_CONVERTER_DIR_ABS_PATH = "변환모듈이 존재하는 디렉토리의 절대경로";
		String FONT_DIR_ABS_PATH = SEDOC_CONVERTER_DIR_ABS_PATH + File.separator + "fonts";
		String TEMP_DIR_ABS_PATH = SEDOC_CONVERTER_DIR_ABS_PATH + File.separator + "temp";
		String SEDOC_CONVERTER_ABS_PATH = SEDOC_CONVERTER_DIR_ABS_PATH + File.separator + "sedocConverter_exe";
		// String SEDOC_CONVERTER_ABS_PATH = SEDOC_CONVERTER_DIR_ABS_PATH + File.separator + "sedocConverter.exe";// window server용
	
		makeDirectory(TEMP_DIR_ABS_PATH);
		makeDirectory(FONT_DIR_ABS_PATH);

		// 변화 명령 구성
		String[] cmd = {SEDOC_CONVERTER_ABS_PATH, "-f", FONT_DIR_ABS_PATH, inputFilePath, outputFilePath, TEMP_DIR_ABS_PATH};
		try {
		    Timer t = new Timer();
		    
			 // JDK 1.7 이상
            ProcessBuilder builder = new ProcessBuilder(cmd);
            // 프로세스의 출력과 에러 스트림을 상속하도록 설정 (부모 프로세스의 콘솔로 출력됨)
            builder.redirectOutput(Redirect.INHERIT);
            builder.redirectError(Redirect.INHERIT);
            
            Process proc = builder.start();
            
            // JDK 1.6 이하   
            /*
            Process proc = Runtime.getRuntime().exec(cmd);
            // 표준 출력 처리
            try (InputStream procInput = proc.getInputStream()) {
			    byte[] buffer = new byte[1024];
			    int bytesRead;
			    while ((bytesRead = procInput.read(buffer)) != -1) {
			        System.out.write(buffer, 0, bytesRead);
			    }
			}
            // 에러 스트림 처리
            try (InputStream procError = proc.getErrorStream()) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = procError.read(buffer)) != -1) {
                    System.err.write(buffer, 0, bytesRead);
                }
            }
            */
		
		    TimerTask killer = new TimeoutProcessKiller(proc);
		    t.schedule(killer, 20000); // 20초 (변환이 20초 안에 완료되지 않으면 프로세스 종료)
		
		    int exitValue = proc.waitFor();
		    killer.cancel();
		
		    return exitValue;
		} catch (Exception e) {
		    e.printStackTrace();
		    return -1;
		}
	}
	
	/**
	 * 문서 모듈 실행 후 변환된 결과를 Serialize 합니다.
	 */
	public static Integer[] serializePbData(String pbFilePath) throws IOException {
	    List<Integer> serializedData = new ArrayList<Integer>();
	    FileInputStream fis = null;
	    InflaterInputStream ifis = null;
	    Integer[] data = null;
	
	    try {
	        fis = new FileInputStream(pbFilePath);
	        fis.skip(16);
	
	        ifis = new InflaterInputStream(fis);
	        byte[] buffer = new byte[1024];
	
	        int len;
	        while ((len = ifis.read(buffer)) != -1) {
	            for (int i = 0; i < len; i++) {
	                serializedData.add(buffer[i] & 0xFF);
	            }
	        }
	
	        data = serializedData.toArray(new Integer[serializedData.size()]);
	    } finally {
	        if (ifis != null) ifis.close();
	        if (fis != null) fis.close();
	    }
	
	    return data;
	}
	
	/**
	 * 파일을 씁니다.
	 */
	private static void writeFile(String path, byte[] bytes) throws IOException {
	    OutputStream os = null;
	    try {
	        os = new FileOutputStream(path);
	        os.write(bytes);
	    } finally {
	        if (os != null) os.close();
	    }
	}
	
	/**
	 * 파일을 삭제합니다.
	 */
	private static void deleteFile(String path) {
		File file = new File(path);
	    if (file.exists()) {
			file.delete();
		}
	}
	
	/**
	 * 디렉토리가 없는 경우 디렉토리를 생성합니다.
	 */
	private static void makeDirectory(String dirPath) {
	    File dir = new File(dirPath);
	    if (!dir.exists()) {
	        dir.mkdir();
	    }
	}
	
	private static class TimeoutProcessKiller extends TimerTask {
	    private Process p;
	
	    public TimeoutProcessKiller(Process p) {
	        this.p = p;
	    }
	
	    @Override
	    public void run() {
	        p.destroy();
	    }
	}
}

3. wmf to png 변환

릴리즈 3.0.2409 이상 릴리즈 2.18.2409 이상

아래한글이나 MS 워드에서 복사하여 붙여넣을 때 WMF 파일이 포함되어 있으면 이를 PNG로 변환합니다.

주의사항

가이드로 제공되는 아래 코드 중 파일 업로드 부분은 샘플 코드로서 보안 관련 처리가 미흡합니다.

파일 업로드 부분은 프로젝트 내부에서 사용하시는 부분을 그대로 사용하시고 아래 코드를 참고하셔서 연동 부분을 처리해주세요. 

wmf to png
package synap.editor.server.controller;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

@Controller
public class WmfToPngController {
    static String IMAGE_UPLOAD_DIR_REL_PATH = "uploads" + File.separator + "image";
    static String OUTPUT_DIR_REL_PATH = "uploads" + File.separator + "output";

    /**
     * 업로드된 WMF 이미지를 PNG로 변환한 후, 변환된 이미지의 다운로드 URL을 반환합니다.
     *
     * @param request          클라이언트의 HTTP 요청 객체
     * @param wmfImageFile     변환할 WMF 이미지 파일
     * @return                 변환된 PNG 파일의 다운로드 URL을 포함한 JSON 응답
     * @throws IOException     파일 저장 또는 변환 중 예외 발생 시
     */
    @PostMapping(value = "/wmfToPng", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    @ResponseBody
    public ResponseEntity<Map<String, String>> convertWmfToPng(HttpServletRequest request, @RequestParam("file") MultipartFile wmfImageFile)
            throws IOException {
        String ROOT_ABS_PATH = request.getSession().getServletContext().getRealPath("");
        String UPLOAD_DIR_ABS_PATH = ROOT_ABS_PATH + File.separator + IMAGE_UPLOAD_DIR_REL_PATH;

        makeDirectory(UPLOAD_DIR_ABS_PATH);

        String fileName = wmfImageFile.getOriginalFilename();
        String inputFileAbsPath = UPLOAD_DIR_ABS_PATH + File.separator + fileName;

        // 업로드된 파일 저장
        writeFile(inputFileAbsPath, wmfImageFile.getBytes());

        // 변환을 위한 작업 디렉토리 생성
        Calendar cal = Calendar.getInstance();
        String yearMonth = String.format("%04d%02d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1);
        String uuid = UUID.randomUUID().toString();
        String worksDirAbsPath = ROOT_ABS_PATH + OUTPUT_DIR_REL_PATH + File.separator + yearMonth + File.separator + uuid;

        makeDirectory(worksDirAbsPath);

        // 이미지 변환
        executeConverter(inputFileAbsPath, worksDirAbsPath + File.separator + "output.png");

        // 변환이 끝난 원본 파일 삭제
        deleteFile(inputFileAbsPath);

        // 다운로드 URL 생성 - 변환된 파일을 사용하는 예시입니다.
        String downloadUrl = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
                + "/download?fileName=" + uuid + "/output.png";
        Map<String, String> response = new HashMap<>();
        response.put("uploadPath", downloadUrl);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        return ResponseEntity
                .status(HttpStatus.OK)
                .headers(headers)
                .body(response);
    }

    /**
     * 변환된 PNG 파일을 클라이언트가 다운로드할 수 있도록 스트리밍합니다.
     *
     * @param fileName         다운로드할 파일의 상대 경로
     * @param request          HTTP 요청 객체
     * @param response         HTTP 응답 객체
     * @throws IOException     파일이 존재하지 않거나 스트리밍 중 오류 발생 시
     */
    @GetMapping("/download")
    public void downloadFile(@RequestParam("fileName") String fileName, HttpServletRequest request, HttpServletResponse response) throws IOException {
        String ROOT_ABS_PATH = request.getSession().getServletContext().getRealPath("");
        Calendar cal = Calendar.getInstance();
        String yearMonth = String.format("%04d%02d", cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1);
        String downloadDirPath = ROOT_ABS_PATH + OUTPUT_DIR_REL_PATH + File.separator + yearMonth + File.separator;

        File file = new File(downloadDirPath + File.separator + fileName);
        if (!file.exists()) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // 파일 다운로드 처리
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
        response.setContentLengthLong(file.length());

        try (InputStream is = new FileInputStream(file);
             OutputStream os = response.getOutputStream()) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
        }
    }

    /**
     * 외부 이미지 변환 도구(sedocConverter)를 호출하여 WMF 파일을 PNG로 변환합니다.
     *
     * @param inputFilePath    변환할 WMF 파일의 절대 경로
     * @param outputFilePath   결과 PNG 파일의 저장 경로
     * @return                 프로세스 종료 코드 (0: 성공, 그 외: 실패)
     */
    public static int executeConverter(String inputFilePath, String outputFilePath) {
        String SEDOC_CONVERTER_DIR_ABS_PATH = "변환모듈이 존재하는 디렉토리의 절대경로";
        String TEMP_DIR_ABS_PATH = SEDOC_CONVERTER_DIR_ABS_PATH + File.separator + "temp";
        String SEDOC_CONVERTER_ABS_PATH = SEDOC_CONVERTER_DIR_ABS_PATH + File.separator + "sedocConverter_exe";
//        String SEDOC_CONVERTER_ABS_PATH = SEDOC_CONVERTER_DIR_ABS_PATH + File.separator + "sedocConverter.exe";// window server용

        makeDirectory(TEMP_DIR_ABS_PATH);

        // 변환 명령 구성
        String[] cmd = {SEDOC_CONVERTER_ABS_PATH, inputFilePath, outputFilePath, TEMP_DIR_ABS_PATH};
        try {
            Timer t = new Timer();
            
			 // JDK 1.7 이상
            ProcessBuilder builder = new ProcessBuilder(cmd);
            // 프로세스의 출력과 에러 스트림을 상속하도록 설정 (부모 프로세스의 콘솔로 출력됨)
            builder.redirectOutput(Redirect.INHERIT);
            builder.redirectError(Redirect.INHERIT);
            
            Process proc = builder.start();
            
            // JDK 1.6 이하   
            /*
            Process proc = Runtime.getRuntime().exec(cmd);
            // 표준 출력 처리
            try (InputStream procInput = proc.getInputStream()) {
			    byte[] buffer = new byte[1024];
			    int bytesRead;
			    while ((bytesRead = procInput.read(buffer)) != -1) {
			        System.out.write(buffer, 0, bytesRead);
			    }
			}
            // 에러 스트림 처리
            try (InputStream procError = proc.getErrorStream()) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = procError.read(buffer)) != -1) {
                    System.err.write(buffer, 0, bytesRead);
                }
            }
            */

            TimerTask killer = new TimeoutProcessKiller(proc);
            t.schedule(killer, 20000);

            int exitValue = proc.waitFor();
            killer.cancel();

            return exitValue;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 파일 경로로부터 바이트 배열을 읽어 반환합니다.
     *
     * @param filePath         읽을 파일의 경로
     * @return                 파일 내용의 바이트 배열
     * @throws IOException     파일 읽기 중 예외 발생 시
     */
    private static byte[] readFileToByteArray(String filePath) throws IOException {
        File file = new File(filePath);
        try (FileInputStream fis = new FileInputStream(file)) {
            return fis.readAllBytes();
        }
    }

    /**
     * 지정한 경로에 바이트 배열을 파일로 저장합니다.
     *
     * @param path             저장할 경로
     * @param bytes            저장할 데이터
     * @throws IOException     파일 쓰기 중 예외 발생 시
     */
    private static void writeFile(String path, byte[] bytes) throws IOException {
        try (OutputStream os = new FileOutputStream(path)) {
            os.write(bytes);
        }
    }

    /**
     * 지정한 경로의 파일을 삭제합니다.
     *
     * @param path             삭제할 파일 경로
     */
    private static void deleteFile(String path) {
        File file = new File(path);
        if (file.exists()) {
            file.delete();
        }
    }

    /**
     * 지정한 경로의 디렉토리가 존재하지 않으면 생성합니다.
     *
     * @param dirPath          생성할 디렉토리 경로
     */
    private static void makeDirectory(String dirPath) {
        File dir = new File(dirPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
    }

    /**
     * 주어진 프로세스를 일정 시간 후 강제 종료하는 TimerTask
     */
    private static class TimeoutProcessKiller extends TimerTask {
        private Process p;

        public TimeoutProcessKiller(Process p) {
            this.p = p;
        }

        @Override
        public void run() {
            p.destroy();
        }
    }
}
  • No labels