정보실

웹학교

정보실

javascript 순수 자바 스크립트로 이미지 업로드 및 드래그를 위한 최고의 가이드

본문

이 가이드에서는 드래그 앤 드롭으로 이미지를 업로드하는 방법을 설명합니다. 

여기에는 이미지 드래그가 포함됩니다


https://blog.soshace.com/the-ultimate-guide-to-drag-and-drop-image-uploading-with-pure-javascript/ 


  • From OS to browser
  • From browser to browser

프레임 워크나 라이브러리없이 Pure Javascript를 사용하며 코드는 IE 9+를 포함한 모든 최신 브라우저와 호환됩니다. 

또한 ES6을 사용하지 않았으므로 코드를 실행하기 위해 Babel과 같은 컴파일러가 필요하지 않습니다.


드래그 앤 드롭 기능은 5 가지 기능을 수행합니다.

  1. Listen for drag and drop
  2. Indicate when a file is hovering on the drop region
  3. Validate dropped images
  4. Preview images
  5. Upload images

그러나 드래그 앤 드롭에 전적으로 의존하는 것은 모바일 사용자가 싫어하기 때문에 나쁜 생각입니다. 

또한 대부분의 모바일 브라우저는 API를 지원하지 않습니다. 

caniuse.com의 Drag & Drop API에 대한 브라우저 지원은 다음과 같습니다.


the browser support for Drag & Drop API 

Drag & Drop API에 대한 브라우저 지원


따라서 사용자는 파일을 선택하여 파일을 간단히 업로드 할 수도 있습니다 (<input type = "file"을 통해).


이 튜토리얼의 최종 결과는 다음과 같습니다.


The Final Result 


JSFiddle에서 데모를 사용할 수 있습니다


기본 HTML부터 시작하겠습니다.



1
2
3
4
5
6
<div id="drop-region">
    <div class="drop-message">
        Drag & Drop images or click to upload
    </div>
    <div id="image-preview"></div>
</div>


그런 다음 요소를 Javascript 변수에 저장하십시오.


1
2
3
4
var // where files are dropped + file selector is opened
    dropRegion = document.getElementById("drop-region"),
    // where images are previewed
    imagePreviewRegion = document.getElementById("image-preview");


파일 선택기 


파일 선택을 위해서는 여기서 <input type = "file">을 사용해야 합니다. 

그러나 이러한 기본 파일 선택기는 구식이며 CSS로 스타일을 지정하기가 어렵습니다. 

따라서 가짜 파일 입력을 사용하여 dropRegoin을 클릭하면 파일 선택기를 열 수 있습니다.


1
2
3
4
5
6
7
8
// open file selector when clicked on the drop region
var fakeInput = document.createElement("input");
fakeInput.type = "file";
fakeInput.accept = "image/*";
fakeInput.multiple = true;
dropRegion.addEventListener('click', function() {
    fakeInput.click();
});


accept 속성 (fakeInput.accept = "image / *")은 파일을 이미지 파일로 제한하는 데 사용됩니다. 

MIME 유형도 지정할 수 있습니다. 예를 들어 GIF 파일 만 선택해야 하는 사용자가 필요한 경우 image / gif를 사용할 수 있습니다. 

또는 image / png, image / jpeg와 같은 여러 값. multiple 속성을 사용하면 한 번에 여러 이미지를 선택할 수 있습니다.


이제 dropRegion을 클릭하면 fakeInput이 클릭됩니다. 따라서 브라우저는 OS 파일 선택기를 엽니다.


파일 입력에 onChange 이벤트를 추가하겠습니다.


1
2
3
4
fakeInput.addEventListener("change", function() {
    var files = fakeInput.files;
    handleFiles(file);
});


fakeInput.files는 이미지를 미리보고 업로드하는 데 사용할 수 있는 FileList입니다. 

나중에 handleFiles() 함수를 만듭니다.


드래그 이벤트 


Drag & Drop API는 8 개의 이벤트를 정의합니다. 드래그 가능한 요소에 대한 4 개의 이벤트와 놓기 가능한 요소에 대한 4 개의 이벤트입니다. 

드래그 앤 드롭 이미지 업로드를 개발할 때 후자 만 필요합니다.

  • dragenter: a dragged item enters a valid drop target.
  • dragleave: a dragged item leaves a valid drop target.
  • dragover: a dragged item is being dragged over a valid drop target. Triggered every few hundred milliseconds.
  • drop: an item is dropped on a valid drop target.

드래그 앤 드롭 기능 추가 


OS에서 파일을 브라우저로 드래그하면 브라우저가 기본적으로 파일을 열어 표시하려고 합니다. 

기본 동작을 방지하고 모든 이벤트에서 상위 요소로의 전파를 중지해야 합니다. 

이렇게 하면 외부 이벤트 (특히 외부 요소의 이벤트)가 기능을 중단 시키지 않습니다.


1
2
3
4
5
6
7
8
9
function preventDefault(e) {
    e.preventDefault();
      e.stopPropagation();
}
 
dropRegion.addEventListener('dragenter', preventDefault, false);
dropRegion.addEventListener('dragleave', preventDefault, false);
dropRegion.addEventListener('dragover', preventDefault, false);
dropRegion.addEventListener('drop', preventDefault, false);


그런 다음 드롭 이벤트를 처리 할 수 ​​있습니다.


1
2
3
4
5
6
7
8
function handleDrop(e) {
    var data = e.dataTransfer,
        files = data.files;
 
    handleFiles(files)      
}
 
dropRegion.addEventListener('drop', handleDrop, false);


e.dataTransfer는 드래그 된 데이터를 포함하는 데이터 전송 객체입니다. 

e.dataTransfer.files는 끌어온 로컬 파일을 FileList로 포함합니다. 

FileList는 파일 입력의 change 이벤트 핸들러에서 files 변수와 정확히 동일합니다.


이제 파일 목록을 가져 와서 각 항목을 업로드하는 handleFiles() 함수를 만들 차례입니다.


1
2
3
4
5
6
function handleFiles(files) {
    for (var i = 0, len = files.length; i < len; i++) {
        if (validateImage(files[i]))
            previewAnduploadImage(files[i]);
    }
}


간단한 for 루프를 사용하여 FileList (여기의 파일)를 반복 할 수 있습니다. 각 파일이 유효한 이미지 인 경우 미리 보고 업로드합니다.


기다림! 문제가 있습니다. 로컬 파일 시스템에서 파일을 드래그 한 경우에만 작동합니다. 다른 웹 페이지에서 이미지를 드래그 하면 어떻게 되나요? 이를 위해 드롭 핸들러를 최적화해야 합니다 (매우 까다롭습니다).


업그레이드 된 handleDrop 기능은 다음과 같습니다.



JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
function handleDrop(e) {
    var dt = e.dataTransfer,
        files = dt.files;
 
    if (files.length) {
 
        handleFiles(files);
 
    } else {
 
        // check for img
        var html = dt.getData('text/html'),
            match = html && /\bsrc="?([^"\s]+)"?\s*/.exec(html),
            url = match && match[1];
 
 
 
        if (url) {
            uploadImageFromURL(url);
            return;
        }
 
    }
 
 
    function uploadImageFromURL(url) {
        var img = new Image;
        var c = document.createElement("canvas");
        var ctx = c.getContext("2d");
 
        img.onload = function() {
            c.width = this.naturalWidth;     // update canvas size to match image
            c.height = this.naturalHeight;
            ctx.drawImage(this, 0, 0);       // draw in image
            c.toBlob(function(blob) {        // get content as PNG blob
 
                // call our main function
                handleFiles( [blob] );
 
            }, "image/png");
        };
        img.onerror = function() {
            alert("Error in uploading");
        }
        img.crossOrigin = "";              // if from different origin
        img.src = url;
    }
 
}


여기에서 파일을 선택하지 않으면 브라우저 이미지를 확인합니다. 브라우저의 이미지를 다른 위치로 드래그 하면 이미지가 HTML로 드래그 됩니다. 따라서 HTML을 가져 와서 이미지 URL 인 src = ""속성을 확인할 수 있습니다. 그런 다음 Image 객체로 이미지를 가져 와서 캔버스로 변환 할 수 있습니다. 마지막으로 캔버스를 블롭으로 변환 할 수 있으며 평소처럼 handleFiles 함수를 사용할 수 있습니다.


그러나 이 구현에는 몇 가지 제한이 있습니다. 

이미지가 도메인 간 요청을 차단하는 서버에서 가져온 경우에는 작동하지 않습니다. 이를 해결하기 위해 프록시 이미지 서버를 사용하여 이미지를 가져올 수 있습니다. 

또한 크롬에서 파이어 폭스로 드래그 하면 오류가 표시 될 수 있습니다. 

이러한 한계에도 불구하고 한 페이지에서 다른 페이지로 드래그 하는 것은 멋진 기능입니다.


이미지 확인 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function validateImage(image) {
    // check the type
    var validTypes = ['image/jpeg', 'image/png', 'image/gif'];
    if (validTypes.indexOf( image.type ) === -1) {
        alert("Invalid File Type");
        return false;
    }
 
    // check the size
    var maxSizeInBytes = 10e6; // 10MB
    if (image.size > maxSizeInBytes) {
        alert("File too large");
        return false;
    }
 
    return true;
}


이 함수는 두 가지 속성을 확인합니다.


1. 파일 형식 –이 예에서는 jpg, png 및 gif 파일을 허용했습니다. 그러나 유효한 MIME 유형을 추가하거나 제거 할 수 있습니다.

2. 파일 크기 – 최대 크기를 10MB로 설정했습니다. 악의적 인 대형 업로드를 방지하기 위해 파일 크기를 제한하는 것이 좋습니다.


클라이언트 측에서 유효성 검사는 한 가지 장점이 있습니다. 사용자는 이미지가 유효한지 즉시 알 수 있습니다. 

서버 측 유효성 검사에 전적으로 의존하는 경우 사용자는 업로드가 완료되고 서버가 응답을 처리 할 때까지 기다려야 합니다. 이미지 파일이 매우 큰 경우 서버에 부담이 될 수 있습니다.


(클라이언트 측에서 파일 크기를 확인하는 것이 부적절합니다. 서버 측에서 파일 크기를 다시 확인해야 합니다.)


이미지 미리보기 및 업로드 


1. 미리보기 


이미지를 미리 보는 방법에는 여러 가지가 있습니다.


1. 업로드 후 미리보기 – 이미지를 업로드하고 이미지 URL을 가져 와서 표시합니다.

2. 업로드하기 전에 미리보기 – 업로드하기 전에 이미지를 미리 볼 수 있습니다. 이것은 빠르고 효율적입니다. 사용할 수 있는 두 가지 자바 스크립트 메소드가 있습니다.


  1. URL.createObjectURL()
  2. FileReader.readAsDataURL()

이 예에서는 FileReader.readAsDataURL()을 사용하여 업로드하기 전에 2.2 : 이미지 미리보기를 선택하겠습니다.


시작하자


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function previewAnduploadImage(image) {
 
    // container
    var imgView = document.createElement("div");
    imgView.className = "image-view";
    imagePreviewRegion.appendChild(imgView);
 
    // previewing image
    var img = document.createElement("img");
    imgView.appendChild(img);
 
    // progress overlay
    var overlay = document.createElement("div");
    overlay.className = "overlay";
    imgView.appendChild(overlay);
 
 
    // ...
}


다음 단계에서 이미지를 읽고 img 요소에 표시합니다. 

오버레이는 업로드 될 때까지 각 이미지에 희미한 모양을 추가하는 데 사용됩니다. 이미지를 업로드 할 때 오버레이 너비를 줄입니다.


이미지를 읽고 미리 봅시다.

1
2
3
4
5
6
// read the image...
var reader = new FileReader();
reader.onload = function(e) {
    img.src = e.target.result;
}
reader.readAsDataURL(image);   


여기에서 FileReader 객체를 만들고 이에 대한 onload 이벤트 핸들러를 설정합니다. 

그런 다음 이미지를 데이터 URL로 읽습니다. 이것은 비동기 적으로 수행됩니다. 

읽기가 끝나면 onload 콜백이 호출됩니다. img 요소의 src 속성은 base64 데이터 URL 인 e.target.result로 설정됩니다.


2. 업로드 


FormData 인터페이스를 사용하여 서버로 전송할 양식 데이터를 작성할 수 있습니다. 

그런 다음 AJAX를 XMLHttpRequest와 함께 사용하여 비동기 업로드를 수행 할 수 있습니다.


양식 데이터 작성 :


1
2
3
// create FormData
var formData = new FormData();
formData.append('image', image);    


AJAX 요청 :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var uploadLocation = 'UPLOAD_LOCATION';
 
var ajax = new XMLHttpRequest();
ajax.open("POST", uploadLocation, true);
 
ajax.onreadystatechange = function(e) {
    if (ajax.readyState === 4) {
        if (ajax.status === 200) {
            // done!
        } else {
            // error!
        }
    }
}
 
ajax.upload.onprogress = function(e) {
 
    // change progress
    // (reduce the width of overlay)
    var perc = (e.loaded / e.total * 100) || 100,
        width = 100 - perc;
 
    overlay.style.width = width;
}
 
ajax.send(formData);


여기서 uploadLocation은 업로드 핸들러의 서버 URL로 설정해야 합니다. 

상태가 변경되면 onreadystatechange 이벤트 핸들러가 호출됩니다. 

요청이 완료되면 ajax.readyState는 4입니다. 

ajax.status는 서버가 전송 한 상태 코드입니다. 

일반적으로 서버는 요청이 성공하면 상태 코드를 200으로 설정합니다. 

진행 상황이 업데이트 될 때마다 ajax.upload.onprogress 이벤트 핸들러가 호출됩니다. 

이 함수에서는 진행률을 계산하여 오버레이 너비에서 줄입니다.


그리고 || 100 부분은 간단한 버그 수정입니다. 때때로 (e.loaded / e.total * 100) NaN을 반환 할 수 있습니다. 이 경우 기본값 100이 사용됩니다.


다음은 완전한 기능입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
function previewAnduploadImage(image) {
 
    // container
    var imgView = document.createElement("div");
    imgView.className = "image-view";
    imagePreviewRegion.appendChild(imgView);
 
    // previewing image
    var img = document.createElement("img");
    imgView.appendChild(img);
 
    // progress overlay
    var overlay = document.createElement("div");
    overlay.className = "overlay";
    imgView.appendChild(overlay);
 
 
    // read the image...
    var reader = new FileReader();
    reader.onload = function(e) {
        img.src = e.target.result;
    }
    reader.readAsDataURL(image);
 
    // create FormData
    var formData = new FormData();
    formData.append('image', image);
 
    // upload the image
    var uploadLocation = 'UPLOAD_LOCATION';
 
    var ajax = new XMLHttpRequest();
    ajax.open("POST", uploadLocation, true);
 
    ajax.onreadystatechange = function(e) {
        if (ajax.readyState === 4) {
            if (ajax.status === 200) {
                // done!
            } else {
                // error!
            }
        }
    }
 
    ajax.upload.onprogress = function(e) {
 
        // change progress
        // (reduce the width of overlay)
 
        var perc = (e.loaded / e.total * 100) || 100,
            width = 100 - perc;
 
        overlay.style.width = width;
    }
 
    ajax.send(formData);
 
}


마지막으로, 여기 제가 만든 CSS가 있습니다. 원하는 대로 변경할 수 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#drop-region {
    background-color: #fff;
    border-radius:20px;
    box-shadow:0 0 35px rgba(0,0,0,0.05);
    width:400px;
    padding:60px 40px;
    text-align: center;
    cursor:pointer;
    transition:.3s;
}
#drop-region:hover {
    box-shadow:0 0 45px rgba(0,0,0,0.1);
}
 
#image-preview {
    margin-top:20px;
}
#image-preview .image-view {
    display: inline-block;
    position:relative;
    margin-right: 13px;
    margin-bottom: 13px;
}
#image-preview .image-view img {
    max-width: 100px;
    max-height: 100px;
}
#image-preview .overlay {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 0;
    right: 0;
    z-index: 2;
    background: rgba(255,255,255,0.5);
}


추가 개선 


1. 드래그 앤 드롭 기능 감지 및 메시지 변경 


드래그 앤 드롭 API를 지원하지 않는 브라우저에서는 "이미지 드래그 앤 드롭 또는 클릭하여 업로드"메시지를 표시하는 것은 유용하지 않습니다. 따라서 기능을 감지하고 메시지를 변경할 수 있습니다.


탐지 기능은 Modernizr에서 가져옵니다.


1
2
3
4
5
6
7
8
9
10
function detectDragDrop() {
    var div = document.createElement('div');
    return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)
}
 
// change the message
var dragSupported = detectDragDrop();
if (!dragSupported) {
    document.getElementsByClassName("drop-message")[0].innerHTML = 'Click to upload';
} 


2. dragenter에 강조 


드래그 이벤트를 사용하여 파일을 드래그 할 때 dropRegion을 강조 표시 할 수 있습니다.


1
2
3
4
5
6
7
8
9
10
11
dropRegion.addEventListener('dragenter', highlight, false);
dropRegion.addEventListener('dragover', highlight, false);
dropRegion.addEventListener('dragleave', unhighlight, false);
dropRegion.addEventListener('drop', unhighlight, false);
 
function highlight() {
    dropRegion.classList.add('highlighted');
}
function unhighlight() {
    dropRegion.classList.remove("highlighted");
}


그런 다음 .highlighted에 대한 일부 CSS는 dropRegion을 더 흥미롭게 만듭니다. 예를 들어


1
2
3
.highlighted {
    background-color:grey;
}


결론 


이 기사에서는 끌어서 놓기를 사용하여 파일을 업로드하는 방법에 대해 설명했습니다. 

끌어서 놓기 기능을 작성하려면 약간의 노력이 필요합니다. 

그러나 jquery와 같은 라이브러리를 사용하면 프로세스가 동일하더라도 DOM 조작이 훨씬 쉽습니다. 

필요한 경우 ES6을 사용하고 구문을 더 짧게 만들 수 있습니다. 

fetch() API는 XMLHttpRequest 대신 사용될 수도 있습니다. 

마지막으로, 내가 만든 JSFiddle을 확인하고 플레이하십시오. 

이미지 업로드에 무료 서비스 IMGBB를 사용했습니다.



  • 트위터로 보내기
  • 페이스북으로 보내기
  • 구글플러스로 보내기
  • 카카오톡으로 보내기

페이지 정보

조회 24회 ]  작성일20-01-23 17:24

웹학교