๐Ÿ’ป ๊ณต๋ถ€ ๊ธฐ๋ก/๐Ÿƒ Spring

Spring | File Upload

  • -

File Upload

๋‹ค์ค‘ ํŒŒ์ผ์„ ์„ ํƒํ•˜์—ฌ ์—…๋กœ๋“œํ•˜๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„

 

์ค‘์š” ๊ธฐ๋Šฅ

- ์—…๋กœ๋“œ

- ์—…๋กœ๋“œ ๊ฒฝ๋กœ

- ์—…๋กœ๋“œ ์ €์žฅ

 

์„ธ๋ถ€ ๊ธฐ๋Šฅ

- ์—…๋กœ๋“œ ์ง„ํ–‰๋ฐ”

- ์—…๋กœ๋“œ ํŒŒ์ผ ํ˜•์‹ ์ œํ•œ(๊ฒ€์ฆ)

- ์—…๋กœ๋“œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ

- ์—…๋กœ๋“œ๋œ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ

 

๊ธฐ์ˆ ์Šคํ…

Java, Spring, JavaScript, JQuery, Ajax, HTML

Apache Tomcat v9.0

 

์—…๋กœ๋“œ | ํ™”๋ฉด

ํ™”๋ฉด์—์„œ ์—…๋กœ๋“œํ•  ํŒŒ์ผ์„ ๊ตฌ์„ฑํ•˜๊ณ  ์„œ๋ฒ„์—์„œ ๋ฐ›์„ ํŒŒ์ผ ํ˜•์‹์„ ์„ค์ •ํ•ด ์ง€์ •๋œ ๊ฒฝ๋กœ๋กœ ์ €์žฅ

 

multiple: ํŒŒ์ผ ์„ ํƒ์‹œ ๋‹ค์ค‘ ํŒŒ์ผ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.
accept: ํŒŒ์ผ ํ™•์žฅ์ž๋ฅผ ์ง€์ •ํ•ด์„œ ์›ํ•˜๋Š” ํ™•์žฅ์ž๋งŒ ๋ณด์ด๊ฒŒ ํ•œ๋‹ค.
<input type="file" id="uploadFiles" multiple accept="image/*, video/*"/>

 

FormData: Ajax๋กœ ํผ ์ „์†ก์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๊ฒŒ ํ•จ
 - JSON ๊ตฌ์กฐ๋กœ "KEY-VALUE" (ํ‚ค์™€ ๊ฐ’) ๊ตฌ์กฐ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†ก
 - ํŽ˜์ด์ง€ ์ „ํ™˜ ์—†์ด ํผ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ์ถœ ํ•˜๊ณ  ์‹ถ์„ ๋•Œ
Ajax: ์„œ๋ฒ„๋กœ ์ €์žฅํ•  url, type, data, xhr ์„ ์ž…๋ ฅ
function uploadFile() {
    var files = document.getElementById('uploadFiles').files;
    var fd = new FormData();
    fd.append('file', files);

    $.ajax({
        url: '/upload',
        data: fd,
        type: 'post',
        contentType: false,
        processData: false,
        xhr: function () {  //XMLHttpRequest ์žฌ์ •์˜ ๊ฐ€๋Šฅ
        	// ํ”„๋กœ๊ทธ๋ ˆ์Šค๋ฐ”
        },
        success: function (ret) { // ์„ฑ๊ณต
        },
        error: function (e) { // ์—๋Ÿฌ
        }
    });
}

 

์—…๋กœ๋“œ | Controller

ํ™”๋ฉด์—์„œ ๋ฐ›์„ ๋ฐ์ดํ„ฐ๋ฅผ ์ง€์ •๋œ ๊ฒฝ๋กœ์— ์ €์žฅํ•  ๋กœ์ง ๊ตฌ์„ฑ

 

@RequestMapping: ๋งคํ•‘ํ•  ์ฃผ์†Œ
UploadFileVO: ๋‹ค์ค‘ ํŒŒ์ผ์„ ๋‹ค๋ฃฐ ํ˜•ํƒœ(Key, Value)
MultipartFile: ๋‹ค์ค‘ ํŒŒ์ผ์˜ ๋‹ค๋ฃจ๋Š” ์ธํ„ฐํŽ˜์ด์Šค
@RequestMapping("/upload")
public UploadFileVO uploadMultipleFileHandler(
            HttpServletRequest request,
            List<MultipartFile> files) {
            
        // UploadFileVO ์ „๋‹ฌ๋ฐ›์„ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ํ˜•ํƒœ(Key, Value)
        // HttpServletRequest ์š”์ฒญ
        // MultipartFile ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด์„œ List ํ˜•ํƒœ๋กœ ๋ฐ›๊ธฐ
}

 

์—…๋กœ๋“œ | ์ €์žฅ ๊ฒฝ๋กœ

๋‹ค์ค‘ ํŒŒ์ผ์„ ์ €์žฅํ•  ๊ฒฝ๋กœ ๊ตฌ์„ฑ

 

UPLOAD_PATH: ์ €์žฅํ•  ์ƒ์œ„ ํด๋” ๊ฒฝ๋กœ
์„œ๋ธ”๋ฆฟ์˜ ๊ธฐ๋ณธ ๋‚ด์žฅ๊ฐ์ฒด request์™€ session์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ๋™ ์ค‘์ธ ์„œ๋ฒ„์˜ ์ ˆ๋Œ€๊ฒฝ๋กœ ์ง€์ •
- getSession(): ํ˜„์žฌ ์„ธ์…˜์ด ์กด์žฌํ•˜๋ฉด ๊ธฐ์กด ์„ธ์…˜์„ ๊ฐ€์ ธ์˜ด
- getServletContext(): ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ์™€ ํ†ต์‹ ํ•˜๊ธฐ ์œ„ํ•œ ์‚ฌ์šฉ ๋ฉ”์„œ๋“œ๋“ค์˜ ํด๋ž˜์Šค
- getRealPath(): ์„œ๋ธ”๋ฆฟ์˜ ๊ฐ€์ƒ๋””๋ ‰ํ† ๋ฆฌ์ƒ์˜ ์‹ค์ œ ๊ฒฝ๋กœ๋ฅผ ์–ป์Œ
private static final String UPLOAD_PATH = "resources"; // path

String applicationPath = request.getSession().getServletContext().getRealPath("");
String uploadFilePath = applicationPath + UPLOAD_PATH;

 

์ €์žฅํ•  ๊ฒฝ๋กœ ์ƒ์œ„ ํด๋”๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ์ƒ์„ฑ
File uploadsPath = new File(uploadFilePath);
// ์—…๋กœ๋“œ ํด๋” ์ƒ์„ฑ(์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ)
if (!uploadsPath.exists()) { // ๊ฒ€์ฆ
    uploadsPath.mkdir(); // uploadFilePath ๊ฒฝ๋กœ์˜ ํด๋” ์ƒ์„ฑ
}

 

์—…๋กœ๋“œ | ์ €์žฅ

FileOutputStream: ๊ธฐ์กด์˜ ํŒŒ์ผ์ด ์—†์œผ๋ฉด ๋งŒ๋“ค์–ด์ง€๊ณ  ์žˆ์œผ๋ฉด ๋ฎ์–ด์“ฐ๊ฒŒ ๋˜์–ด ๊ธฐ์กด ํŒŒ์ผ๋‚ด์šฉ์ด ์ง€์›Œ์ง
BufferedOutputStream: byte๋‹จ์œ„๋กœ ํŒŒ์ผ์„ ๊ธฐ๋ก ํ• ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฒ„ํผ ์ŠคํŠธ๋ฆผ
if (originalFileName != null) { // ํŒŒ์ผ ์ด๋ฆ„ ์œ ๋ฌด ๊ฒ€์ฆ
    // ํŒŒ์ผ ์ €์žฅ(์ €์žฅ ํด๋” ๊ฒฝ๋กœ + OSํƒ€์ž… ํ˜•ํƒœ๋ณ€ํ™˜(File.separator) + ํŒŒ์ผ ์ด๋ฆ„)
    File serverFile = new File(uploadFilePath + File.separator + savedFileName);
    // FileOutputStream : ๊ธฐ์กด์˜ ํŒŒ์ผ์ด ์—†์œผ๋ฉด ๋งŒ๋“ค์–ด์ง€๊ณ  ์žˆ์œผ๋ฉด ๋ฎ์–ด์“ฐ๊ฒŒ ๋˜์–ด ๊ธฐ์กด ํŒŒ์ผ๋‚ด์šฉ์ด ์ง€์›Œ์ง
    // BufferedOutputStream : byte๋‹จ์œ„๋กœ ํŒŒ์ผ์„ ๊ธฐ๋ก ํ• ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฒ„ํผ ์ŠคํŠธ๋ฆผ
    BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(serverFile));
    // ๋ฌธ์ž์—ด์„ ๋ฐ”์ดํŠธ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ํ•ด์„œ ํŒŒ์ผ์— ์ €์žฅํ•œ๋‹ค.
    stream.write(bytes);
    // ์‚ฌ์šฉ์ด ๋๋‚˜๋ฉด ํŒŒ์ผ ์ŠคํŠธ๋ฆผ ๋‹ซ๊ธฐ
    stream.close();
}

 


 

์—…๋กœ๋“œ | Progress(ํ”„๋กœ๊ทธ๋ ˆ์Šค)

๋ธŒ๋ผ์šฐ์ € ๋‚ด์žฅ ๊ฐ์ฒด XMLHttpRequest ๋ฅผ ์ด์šฉํ•œ ํ”„๋กœ๊ทธ๋ ˆ์Šค ์‚ฌ์šฉ

 

HTML ์˜ ์š”์†Œ ์ค‘ ํ•˜๋‚˜๋กœ ์ž‘์—…์˜ ์™„๋ฃŒ ์ •๋„๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ์ฃผ๋กœ ์ง„ํ–‰ ํ‘œ์‹œ์ค„์˜ ํ˜•ํƒœ๋กœ ์‚ฌ์šฉํ•จ
<progress id="progressBar" value="0" max="100" style="width:100%"></progress>

 

Ajax ํ†ต์‹ ์„ ์‚ฌ์šฉํ•ด์„œ ์—…๋กœ๋“œ ์ง„ํ–‰์ƒํ™ฉ์„ ํ”„๋กœ๊ทธ๋ ˆ์Šค๋ฅผ ํ†ตํ•ด ํ‘œ์‹œ
// ์ถœ์ฒ˜ | https://coderwall.com/p/je3uww/get-progress-of-an-ajax-request
$.ajax({
    url: path,
    type: 'post',
    data: {payload: payload},
    xhr: function () {
        var xhr = $.ajaxSettings.xhr();
        xhr.onprogress = function e() {
            // For downloads
            if (e.lengthComputable) {
                console.log(e.loaded / e.total);
            }
        };
        xhr.upload.onprogress = function (e) {
            // For uploads
            if (e.lengthComputable) {
                var percent = e.loaded * 100 / e.total;
                setProgress(percent); // progress input val
            }
        };
        return xhr;
    }
}).done(function (e) {
    // Do something
}).fail(function (e) {
    // Do something
});

// progress ํƒœ๊ทธ ๊ฐ’ ์ž…๋ ฅ
var $progressBar = $("#progressBar");
function setProgress(per) {
	$progressBar.val(per);
}

 

์—…๋กœ๋“œ | ํŒŒ์ผ ์—…๋กœ๋“œ ๊ฒ€์ฆ

ํŒŒ์ผ ์ˆ˜์™€ ํŒŒ์ผ์˜ ํ™•์žฅ์ž ๋“ฑ์˜ ๊ฒ€์ฆ ์ฒ˜๋ฆฌ

 

ํŒŒ์ผ ์ˆ˜ ๊ฒ€์ฆ
ํŒŒ์ผ ํ™•์žฅ์ž ๊ฒ€์ฆ
const uploadFiles = document.getElementById('uploadFiles');

// addEventListener๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐ˜์‘ ํ›„ ์‹คํ–‰
uploadFiles.addEventListener('change', updateImageDisplay);

function updateImageDisplay() {
    // ํŒŒ์ผ ์ •๋ณด
    const curFiles = uploadFiles.files;
    
    if (curFiles.length === 0) { // ํŒŒ์ผ์ด ์—†์œผ๋ฉด ์•ˆ๋‚ด ๋ฉ”์„ธ์ง€ ์ „๋‹ฌ
        const para = document.createElement('p');
        para.textContent = '์„ ํƒ๋œ ํŒŒ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค.';
        return uploadFilePreview.appendChild(para);
    }
    
    // ํŒŒ์ผ ํ™•์žฅ์ž ๊ฒ€์ฆ
    validFileType(file);
}

// ํŒŒ์ผ ํƒ€์ž… ํ•ญ๋ชฉ
const fileTypes = [
    'image/apng',
    'image/bmp',
    'image/gif',
    'image/jpeg',
    'image/pjpeg',
    'image/png',
    'image/svg+xml',
    'image/tiff',
    'image/webp',
    'video/mp4',
];

// ํŒŒ์ผ ํƒ€์ž… ๊ฒ€์‚ฌ
function validFileType(file) {
    return fileTypes.includes(file.type);
}

 


 

์žผ๋ฐŒ๋‹ค.

Contents

ํฌ์ŠคํŒ… ์ฃผ์†Œ๋ฅผ ๋ณต์‚ฌํ–ˆ์Šต๋‹ˆ๋‹ค

์ด ๊ธ€์ด ๋„์›€์ด ๋˜์—ˆ๋‹ค๋ฉด ๊ณต๊ฐ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค.