• 목록
  • 아래로
  • 위로


        obj.on('drop', function (e) {
            e.preventDefault();
            var files = e.originalEvent.dataTransfer.files;
            for (var i = 0; i < files.length; i++) {
                ext = files[i].name.split('.').pop();
                if (ext != 'jpg' && ext != 'jpeg' && ext != 'png' && ext != 'gif') {
                    alert(ext + ' filetype is not allowed!');
                    throw new Error('Filetype error.');
                }
            }
            if(files.length < 1)
                return;
                                     
            for (var i = 0; i < files.length; i++) {
                var data = new FormData(); // 이 부분에 대해 질문을 드립니다!!!
                data.append('up_load', files[i]);
                $.ajax({
                    url: '/bbs/dropbox.php',
                    method: 'post',
                    data: data,
                    async: true,
                    processData: false,
                    contentType: false,
          success : function(datas) {
            $('#dropzone')
                        .css('border', '1px dashed blue')
                        .css('cursor', 'pointer');
        $("input#file").val("");
        var filename = datas.match(/.*?_.*?_(.+)\?raw=1/)[1];
        $("#img_list").append("<img src='" + datas + "' class='dropbox_upload' data-toggle='tooltip' data-placement='bottom' title='" + filename + "' />");
        img_focus();
                    }
                });
            }
        })



안녕하세요?


자바스크립트의 FormData와 관련하여 질문 드립니다.


API가 멀티파일 업로드를 허용하지 않는 상황에서 위와 같은 방식으로 복수의 파일을 drag & drop을 하면 


for문으로 ajax를 돌려서 하나씩 파일을 전송하려고 하는데요.



원래는 var data = new FormData();를 for문 밖에 놓고 


data.set('up_load', files[i]);라고 하여 값을 덮어썼는데요.


모던 브라우저에서는 문제가 없는데 IE에서 작동을 안 하길래 확인해보니 IE에서는 set 메서드를 지원하지 않네요 ㅠㅠ

(IE 11에서 테스트했습니다)


게다가 IE에서는 delete 메서드도 지원하지 않구요.



그래서 IE의 호환성 때문에 부득이 다음과 같이 for문을 돌릴 때마다 data 변수를 새로 생성하는 방식을 택했는데 이것이 최선의 방법일까요?


var data = new FormData();

data.append('up_load', files[i]);


제가 자바스크립트 & jQuery 실력이 부족하다보니 보다 효율적인 방법이 있을 것 같아서 여쭤봅니다 ^^


이런 부분의 처리와 관련하여 넓은 의미에서의 코딩 컨벤션이 있을 것 같아서요.



그럼 폭염경보가 발령되었다는데 건강 유의하시고 좋은 오후 되시고, 이번 한 주도 화이팅입니다 ^-^


답변 달아주실 부들께 미리 감사드려요~



https://developer.mozilla.org/ko/docs/Web/API/FormData



작성자
이니스프리 119 Lv. (2%) 4190880/115200000EXP

Make StudyForUs Great Again!

 

CSVpuymXAAAVVpd.jpg

댓글 6

Hanam09
profile image
obj.addEventListener("drop",e => {
/* Vanila JS 로 작성
 * 하지만 속도를 올려보려 할 수 있죠
 */
e.preventDefault();
var files = e.originalEvent.dataTransfer.files;
var allowedExt = /(jpg|jpeg|png|gif|webp|bmp)/i;
for (var count = 0; count < files.length; count++) {
extension = files[count].name.split(".").pop();
if (!allowed.test(extension)) {
window.alert(extension+" filetype isn't allowed!");
throw new Error("Filetype Error.");
}
}
if (files.length < 1) return;

for (var i = 0; i < files.length; i++) {
var data = new FormData();
var xmlHttp = new XMLHttpRequest();
data.append("up_load",files[i]);
xmlHttp.open("POST","/bbs/dropbox.php",true);
xmlHttp.send(data);
xmlHttp.onreadystatechange = function() {
if (this.readyState === this.DONE && this.status === 200) {
var filename = this.response.match(/.*?_.*?_(.+)\?raw=1/)[1];
document.getElementById('dropzone').style.border = "1px dashed blue";
document.getElementById('dropzone').style.cursor = "pointer";
document.querySelector('input#file').value = "";
document.getElementById('img_list').innerHTML += "<img src='" + this.responseText + "' class='dropbox_upload' data-toggle='tooltip' data-placement='bottom' title='" + filename + "'>";
img_focus();
}
}
}
});



음  값을 바꾸긴 어렵겠네요!




comment menu
2019.08.05. 23:40

신고

"Hanam09님의 댓글"

이 댓글을 신고 하시겠습니까?

이니스프리 작성자 → Hanam09
profile image

안녕하세요?
날씨도 덥고 바쁘신데 바닐라 자바스크립트로 다시 작성해주셔서 감사합니다!!

formdata에 append 메서드를 사용하는 것이 IE의 호환성 관련하여 최선이라는 말씀이시죠? ^^


개발자분들의 커뮤니티에 올라오는 글을 보면 요샌 jQuery를 버리려는 경향이 있더군요~

저는 빠른 개발(이라고 쓰지만 실제로는 개발할 능력이 안 되어서 구글링 후 복붙)을 하느라  jQuery를 불필요하게 남발한 것 같네요.

덕분에 많이 배우고 갑니다 ^-^


+)

아참 죄송한데 하나만 더 여쭤볼게요~


for문 안에 ajax의 success가 들어가서 발생하는 문제 같은데요.

$('#dropzone') 

    .css('border', '1px dashed blue') 

    .css('cursor', 'pointer');

이 부분 때문에 여러 개의 파일 중에 하나만 전송이 완료되어도 CSS 속성이 변경되는데요.

혹시 여러 개의 파일 모두가 전송이 완료된 후에 CSS 속성을 변화시키는 방법이 있을까요?


$.ajax를 사용해서 통신을 하면 $.ajax가 가장 마지막에 호출되어 동작하기 때문인지

$.ajax가 포함된 for문의 뒤에 $("body").css("cursor", "default");을 넣으면 

커서가 progress로 아예 바뀌지 않는 것처럼 보이더군요 ㅠㅠ

comment menu
2019.08.06. 00:19

신고

"이니스프리님의 댓글"

이 댓글을 신고 하시겠습니까?

Hanam09 → 이니스프리
profile image

XHR을 HTTP synchronous request 요청을 사용하면 구현이 정말 쉽지만 AJAX를 사용하면  비동기라는 특성때문에 구현이 약간 힘듭니다.


obj.addEventListener("drop",e => {
/* Vanila JS 로 작성
 * registedLastReq라는 전역변수로 for문의 마지막에서 Ajax의 전송대기열에 올린것을 선언하고 success에서 이를 구별함.
 */
e.preventDefault();
var files = e.originalEvent.dataTransfer.files;
var allowedExt = /(jpg|jpeg|png|gif|webp|bmp)/i;
registedLastReq = false;
for (var count = 0; count < files.length; count++) {
extension = files[count].name.split(".").pop();
if (!allowed.test(extension)) {
window.alert(extension+" filetype isn't allowed!");
throw new Error("Filetype Error.");
}
}
if (files.length < 1) return;

for (var i = 0; i < files.length; i++) {
var data = new FormData();
var xmlHttp = new XMLHttpRequest();
data.append("up_load",files[i]);
xmlHttp.open("POST","/bbs/dropbox.php",true);
xmlHttp.send(data);
if (i == files.length-1)
registedLastReq = true;

xmlHttp.onreadystatechange = function() {
if (this.readyState === this.DONE && this.status === 200) {
var filename = this.response.match(/.*?_.*?_(.+)\?raw=1/)[1];
document.querySelector('input#file').value = "";
document.getElementById('img_list').innerHTML += "<img src='" + this.responseText + "' class='dropbox_upload' data-toggle='tooltip' data-placement='bottom' title='" + filename + "'>";
img_focus();
if (registedLastReq) {
document.getElementById('dropzone').style.border = "1px dashed blue";
document.getElementById('dropzone').style.cursor = "pointer";registedLastReq=false;
}
}
}

}
});



음.. 일단 이렇게 해봤어요... 


이 코드의 문제점은 이 Ajax가 비동기요청으로 작동한다는 겁니다.

for 문에서 마지막 ajax전송부분까지 요청을 Send했다고 하더라도.. 

그 요청은 아직 완료된게 아닙니다. 요청대기열에 올렸을 뿐이죠...

그래서 마지막 요청을 요청대기열에 올리고 registedLaseReq라는 변수를 선언하여 success될때 이를 구별한다 하더라도 마지막 요청의 앞에

대기타고있는 요청이 있다면 그 요청이 이 변수를 가로채서(...) 완료되었다고 치고 스타일을 바꿀 수 있는거죠....


그래서 이를 좀 개선한 코드를 추가해봤습니다...



obj.addEventListener("drop",e => {
/* Vanila JS 로 작성
 *
 */
e.preventDefault();
var files = e.originalEvent.dataTransfer.files;
var allowedExt = /(jpg|jpeg|png|gif|webp|bmp)/i;
for (var count = 0; count < files.length; count++) {
extension = files[count].name.split(".").pop();
if (!allowed.test(extension)) {
window.alert(extension+" filetype isn't allowed!");
throw new Error("Filetype Error.");
}
}
if (files.length < 1) return;

for (var i = 0; i < files.length; i++) {
var data = new FormData();
var xmlHttp = new XMLHttpRequest();
data.append("up_load",files[i]);
xmlHttp.open("POST","/bbs/dropbox.php",true);
xmlHttp.send(data);
if (i == files.length-1) {
xmlHttp.onreadystatechange = function() {
if (this.readyState === this.DONE && this.status === 200) {
var filename = this.response.match(/.*?_.*?_(.+)\?raw=1/)[1];
document.getElementById('dropzone').style.border = "1px dashed blue";
document.getElementById('dropzone').style.cursor = "pointer";
document.querySelector('input#file').value = "";
document.getElementById('img_list').innerHTML += "<img src='" + this.responseText + "' class='dropbox_upload' data-toggle='tooltip' data-placement='bottom' title='" + filename + "'>";
img_focus();
}
}
} else {
xmlHttp.onreadystatechange = function() {
if (this.readyState === this.DONE && this.status === 200) {
var filename = this.response.match(/.*?_.*?_(.+)\?raw=1/)[1];
document.querySelector('input#file').value = "";
document.getElementById('img_list').innerHTML += "<img src='" + this.responseText + "' class='dropbox_upload' data-toggle='tooltip' data-placement='bottom' title='" + filename + "'>";
img_focus();
}
}
}

}
});



이건 좀 무식한 방법입니다...

for문에서 if문으로 마지막 요청을 구별하여 스타일하나 바꾸기위해 통으로 복붙한 후  마지막 요청 success에 스타일을 바꾸는 코드를 삽입한겁니다.

하지만 이짓때문에  success 같은 코드가 생겼습니다.....


success에서 마지막 요청을 구별할 수 있는 방법은 없느냐? 없습니다...


이런저런 문제(비동기적  작동, 클로저)가 있어서 우리가 상상하는 코드는 못짤겄같습니다.


하지만 비동기만 포기하면 말이 좀 달라집니다.




obj.addEventListener("drop",e => {
/* Vanila JS 로 작성
 * Synchronous request
 */
e.preventDefault();
var files = e.originalEvent.dataTransfer.files;
var allowedExt = /(jpg|jpeg|png|gif|webp|bmp)/i;
for (var count = 0; count < files.length; count++) {
extension = files[count].name.split(".").pop();
if (!allowed.test(extension)) {
window.alert(extension+" filetype isn't allowed!");
throw new Error("Filetype Error.");
}
}
if (files.length < 1) return;

for (var i = 0; i < files.length; i++) {
var data = new FormData();
var xmlHttp = new XMLHttpRequest();
data.append("up_load",files[i]);
xmlHttp.open("POST","/bbs/dropbox.php",false);
xmlHttp.send(data);
if (xmlHttp.status === 200) {
var filename = this.response.match(/.*?_.*?_(.+)\?raw=1/)[1];
if (i === files.length-1) {
document.getElementById('dropzone').style.border = "1px dashed blue";
document.getElementById('dropzone').style.cursor = "pointer";
}
document.querySelector('input#file').value = "";
document.getElementById('img_list').innerHTML += "<img src='" + this.responseText + "' class='dropbox_upload' data-toggle='tooltip' data-placement='bottom' title='" + filename + "'>";
img_focus();
}
}
});


이 정도로 코드가 맑아집니다(...)


다만 동기적요청은  일부 웹 엔진에서  사용자 경험에 좋지않은 영향을  발생시킬 수 있다는 이유로 비권장 사항입니다. 다만 Naver도 씁니다.


참고로.... 동기요청에서 서버가 빨리 응답 안해주면 코드 실행자체가 얼어붙습니다.

comment menu
2019.08.06. 16:41

신고

"Hanam09님의 댓글"

이 댓글을 신고 하시겠습니까?

이니스프리 작성자 → Hanam09
profile image

오오~ 날씨도 무더운데 상세한 설명을 해주셔서 정말 감사드립니다! ^-^

바닐라 자바스크립트로 보니 코드 자체는 길어져도 오히려 이해는 쏙쏙 잘 되네요~

 

결과적으로 synchronous로 처리하면 간단히 해결되는 문제이군요 ㄷㄷ

어차피 Dropbox API가 멀티파일 업로드를 허용하지 않아서

결국 제 서버의 PHP cURL과 Dropbox 서버와의 통신은 사실상 동기적으로 이루어질텐데 말이죠.

 

이 스크립트를 작성하면서 이런저런 문제로 jQuery ajax에서 async: false로 설정을 변경한 적이 있었는데요.

(명쾌하게 이해는 안 되지만 ajax 구문만 따로 함수로 선언해서 호출할 때에는

async를 끄지 않으면 완전 뒤죽박죽이 되어버려서 부득이 false로 설정해야 되더군요 ㅜㅜ)

크롬 최신버전에서는 depreciated라는 warning이 뜨면서도 작동은 하는 경우도 있고, 아예 멈추는 경우도 있고 케바케더군요.

(제가 테스트한 환경에서는 파폭 최신버전으로는 작동은 잘 되는 것 같더군요)

이에 대해 구체적으로 크롬에서 어떤 경우에 작동을 멈추는지에 대해서는 구글링을 해봐도 명확한 답이 없더군요 ㅠㅠ

 

모든 문제는 제가 자바스크립트의 동작방식과 ajax가 어떻게 돌아가는지에 대해 잘 모르기 때문인 것 같네요.

(자꾸 PHP나 파이썬 돌아가듯이 생각하는 경향이 있어서요 ㅠㅠ)

애당초 ajax를 for문으로 반복을 돌리는 것이 아니라 선택된 여러 개의 파일을 한꺼번에 PHP로 넘기고

PHP에서 반복문으로 cURL을 돌리는 것이 보다 간명한 방법이라는 생각이 들기는 하는데

이제 와서 그렇게 수정하려면 상당히 많이 뜯어고쳐야겠네요 ㅜㅜ

 

일단 올려주신 스크립트를 바탕으로 더 공부하면서 계속 수정해보겠습니다!

책을 사놓고 제대로 읽지 못했는데 클로저에 대한 공부도 더 해야겠네요~

다시 한 번 감사드립니다 ^^

그럼 저녁식사 맛있게 드세요~

comment menu
2019.08.06. 18:23

신고

"이니스프리님의 댓글"

이 댓글을 신고 하시겠습니까?

Hanam09 → 이니스프리
profile image

음.. 제가 솔직히 말하자면... 동기요청을 추천드리지는 않습니다.

물론 서버가 에러처리 잘하고 요청을 값넘기고 요청을 빠르게 닫는다면 정말 좋은 방식이긴 합니다.

문제는 서버 지연속도에 따라 클라이언트 쪽도 덩달아 얼어버리는 상황이 발생될 수 있습니다.

무슨뜻이냐면, a 라는 브라우저가 동기 XHR로 서버에 요청했을때, 서버가 값을 주고 요청을 닫기 전까지는 클라이언트는 그 어떤 js도 실행시킬 수 없습니다. 서버가 응답을 안해서 아직 finished상태가 되지 않았고 js 인터프리터는 구문을 순차적으로 실행하기 때문에 작업이 완료되지 않으면 새로운 작업을 시작할 수 없습니다. 그래서 비동기작업이 존재하는거구요.

 

그럼,

Good night :)

comment menu
2019.08.06. 22:25

신고

"Hanam09님의 댓글"

이 댓글을 신고 하시겠습니까?

이니스프리 작성자 → Hanam09
profile image

옙 잘 알겠습니다! 번번이 많이 배워서 감사합니다 ^^

당장 불편하게 느껴지더라도 depreciated 된 것은 나름대로의 이유가 있겠죠.

 

CSS & 자바스크립트 애니메이션이 제대로 작동하게 하려면

결국 ajax로 한꺼번에 파일들을 넘기고 PHP에서 cURL을 반복문으로 돌리는 방향으로 새로 짜는 것이 가장 바람직하겠네요~

멀티파일 업로드를 지원하지 않는 API를 클라이언트 단에서는 멀티파일을 선택하는 방식으로 작동하게 하는 것을

제가 처음 경험하다보니 시행착오가 꽤 있었네요 ㅠㅠ

 

그럼 오늘 밤에 태풍이 온다는데 비 조심하시고 굿밤 되세요!!

덕분에 이것저것 많이 배워서 다시 한 번 감사드립니다 :)

comment menu
2019.08.07. 00:30

신고

"이니스프리님의 댓글"

이 댓글을 신고 하시겠습니까?

권한이 없습니다.
번호 제목 글쓴이 날짜 조회 수
공지 시스템 점검 작업 완료 안내 10 마스터 24.09.05.16:25 2644
공지 [중요] 호스팅 만료와 관련하여 일부 수칙이 변경됩니다. 4 마스터 23.01.14.02:23 10096
공지 [필독] 질문하는 방법 17 마스터 18.02.23.03:09 4957
607 유튜브 페이지 가져오는 방법이 뭐가있을까요? 9 image 슬기 19.10.11.16:52 925
606 워드프레스 백업 툴 추천 부탁드려요! 10 갱생협스 19.10.04.19:53 329
605 일본어 시험 초급(?)을 따야되는데 조언을 부탁드립니다 ㅠㅠ 6 이니스프리 19.10.04.12:06 502
604 리눅스에서.. 무선랜카드 활성화?켜기? 3 홀민 19.09.10.02:26 446
603 [미디어위키] 시각편집기 no vrs 260578 19.09.03.19:49 258
602 호스팅패널에서 서브도메인 사용하시는 분 계신가요? 2 joyfuI 19.08.28.18:13 403
601 스포어에서 허용하는 이미지 업로드 확장자 및 ios에서의 input 태그 줌과 관련하여 여쭤봅니다 2 이니스프리 19.08.27.23:04 405
600 Nginx 이미지 외부링크 방지 설정 여부를 확인하는 방법 2 이니스프리 19.08.26.18:48 492
599 Beautifulsoup 로그인 문제 18 Hanam09 19.08.24.01:25 1798
598 부트스트랩 모달의 사이즈를 구해서 사이즈에 맞게 이모지 n개/행을 출력하려고 하는데요. 5 image 이니스프리 19.08.23.20:50 659
597 Jquery 사용 시 $(window).load( function() 를 사용하시나요? 5 로우지 19.08.20.03:15 239
596 ftp.studyforus.com은 서비스를 중지하신건가요? 2 260578 19.08.19.18:15 293
595 출첵을 했는데 처리가 제대로 안됐어요! image 국내산라이츄 19.08.19.00:01 259
594 imgur 사용하시는분.... 잘아시는분...질문! 5 홀민 19.08.18.22:08 585
593 a태그를 특정 조건에서만 비활성화시키고 싶은데... +사소한 기능 문제 6 image 국내산라이츄 19.08.08.16:44 875
592 이거 도메인 사용 가능한 거 맞죠? 13 image 국내산라이츄 19.08.08.11:31 515
591 자바스크립트는 왜 이럴까요... 5 image 국내산라이츄 19.08.07.16:06 561
590 창 크기에 따라서 표의 크기가 달라지는 태그는 없나요? 6 image 국내산라이츄 19.08.05.17:41 455
자바스크립트 FormData와 관련된 메서드의 IE 호환성과 관련하여 질문 드립니다 ^^ 6 image 이니스프리 19.08.05.14:22 3504
588 프로알라 에디터를 사용하면 클라이언트단에서 이미지 리사이징을 할 수 있는가요? 6 이니스프리 19.08.02.16:00 422