Development/Java

[Library] 오렐리 COS 라이브러리 이미지 다중 업로드

컴공희 2024. 5. 4. 20:42

문제

  • 저장은 모두 저장되지만, 한 개 파일만 순차적으로 읽을 수 있음.
  • 파일이름을 while (files.hasMoreElements()) {}  을 통해서 모두 읽을 수 없음 .
  • 파일이름을 통해 DB에 파일이름을 저장해야 했기 때문에 모든 파일의 정보가 필요했다.

 


 

 

소개

서블릿에서 이미지 업로드를 검색하면 가장 많이 나오는 라이브러리 중 하나가 오렐리의 COS 라이브러리이다. 

 

엄청 오래되어 보이는 냄새..

 

이미지 업로드 구현을 위해서 다중 업로드 기능을 만들던 중 

위에 처럼 다중 선택이 가능하도록 만들려고 코드에 multiple을 추가했다. 

<input type="file" id="files" name="files" multiple>

이렇게 추가하면 file에 대한 값이 여러개 입력할 수 있도록 값이 전달된다. 

-----------------------------1234567890123456789012345678
Content-Disposition: form-data; name="files[]"; filename="file1.txt"
Content-Type: text/plain

[내용이 여기에 들어갑니다. 파일 1의 내용]
-----------------------------1234567890123456789012345678
Content-Disposition: form-data; name="files[]"; filename="file2.txt"
Content-Type: text/plain

[내용이 여기에 들어갑니다. 파일 2의 내용]
-----------------------------1234567890123456789012345678--

 

하지만,  오렐리 COS 라이브러리를 사용하면 파일은 모두 저장되지만, 
Enumeration files = mr.getFileNames()  했을 땐 항상 한 개만 들어있었다. 

다른 인터넷 자료 처럼,  각각 다른 이름으로 여러개의 input type을 만들어서 했을 때는 문제 없이 작동한다.

<input type="file" id="files1" name="files1">
<input type="file" id="files2" name="files2">

 

 

 

원인

오렐리의 라이브러리를 직접 들어가보면 files는 name을 기준으로 담는다.

<input type="file" id="files" name="files" multiple>

로 입력한다면 같은 이름으로 파일을 넣게되니 마지막 파일이 덮어써져 한 개만 남게 되는 것이다. 

 

 

 

해결 방안

오렐리를 사용하지 않고 아파치가 제공하는 Common FileUpload를 사용하자.

 

 

이전 코드

public static Map<String, String> fileSave(HttpServletRequest req) {
        try {
            Map<String, String> param = new HashMap<>();
            List<String> fileNames = new ArrayList<>();

            MultipartRequest mr = new MultipartRequest(req, FILE_PATH, 400000, "UTF-8", new DefaultFileRenamePolicy());
            Enumeration params = mr.getParameterNames();

            while (params.hasMoreElements()) {
                String name = (String) params.nextElement();
                String value = mr.getParameter(name);
                log.info("name = {} value ={}", name, value);
                param.put(name, value);
            }

            Enumeration files = mr.getFileNames();

            while (files.hasMoreElements()) {
                String name = (String) files.nextElement();
                String fileName = mr.getFilesystemName(name);
                String original = mr.getOriginalFileName(name);
                String type = mr.getContentType(name);
                File file = mr.getFile(name);

                param.put("fileName", fileName);
                fileNames.add(original);

                log.info("요청 파라미터 이름 : {}", name);
                log.info("실제 파일 이름 : {} ", original);
                log.info("저장 파일 이름 : {}", fileName);
                log.info("파일 콘텐츠 유형 : {}", type);

                if (file != null) {
                    log.info("파일 크기 : {}", file.length());
                }
            }
            req.setAttribute("fileNames", fileNames);
            return param;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

 

 

이후 코드 

public static Map<String, String> fileSave(HttpServletRequest req) {
        try {
            Map<String, String> param = new HashMap<>();
            List<String> fileNames = new ArrayList<>();

            // Create a factory for disk-based file items
            DiskFileItemFactory factory = new DiskFileItemFactory();

            // Set factory constraints
            factory.setSizeThreshold(400000); // Set the maximum allowed size (in bytes) before a FileUploadException will be thrown.
            factory.setRepository(new File(System.getProperty("java.io.tmpdir"))); // Set the repository location where files will be stored temporarily.

            // Create a new file upload handler
            ServletFileUpload upload = new ServletFileUpload(factory);

            // Parse the request
            List<FileItem> items = upload.parseRequest(req);

            // Process the uploaded items
            for (FileItem item : items) {
                if (item.isFormField()) { // Process regular form fields
                    String name = item.getFieldName();
                    String value = item.getString("UTF-8"); // Ensure UTF-8 encoding
                    log.info("name = {} value ={}", name, value);
                    param.put(name, value);
                } else { // Process uploaded files
                    String fileName = item.getName();
                    String type = item.getContentType();
                    File file = new File(FILE_PATH, fileName);

                    try (InputStream fileContent = item.getInputStream();
                         OutputStream outputStream = new FileOutputStream(file)) {
                        IOUtils.copy(fileContent, outputStream); // Apache Commons IOUtils is used for stream copying
                    }

                    param.put("fileName", fileName);
                    fileNames.add(fileName);

                    log.info("실제 파일 이름 : {} ", fileName);
                    log.info("저장 파일 이름 : {}", file.getName());
                    log.info("파일 콘텐츠 유형 : {}", type);
                    log.info("파일 크기 : {}", file.length());
                }
            }
            req.setAttribute("fileNames", fileNames);
            return param;
        } catch (FileUploadException | IOException e) {
            throw new RuntimeException(e);
        }
    }

위는 gpt가 만들어준 코드... 놀랍다!

 

 

 

오늘도 삽집을 통해 배웠다... 

어쩌면 난 노가다꾼이 아닌가?