도와주세요

자바스크립트 FormData와 관련된 메서드의 IE 호환성과 관련하여 질문 드립니다 ^^

이니스프리2019.08.05 14:22조회 수 81댓글 6

  • 1
    • 글자 크기


        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



ཇོ་མོ་གླང་མ

  • 1
    • 글자 크기
창 크기에 따라서 표의 크기가 달라지는 태그는 없나요? (by 왕뚠뚠돈까스라이츄) 프로알라 에디터를 사용하면 클라이언트단에서 이미지 리사이징을 할 수 있는가요? (by 이니스프리)
  • 2019.8.5 23:40
    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();
    }
    }
    }
    });



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




  • @Hanam09
    이니스프리글쓴이
    2019.8.6 00:19

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

    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로 아예 바뀌지 않는 것처럼 보이더군요 ㅠㅠ

  • @이니스프리
    2019.8.6 16:41

    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도 씁니다.


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

  • @Hanam09
    이니스프리글쓴이
    2019.8.6 18:23

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

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

     

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

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

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

     

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

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

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

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

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

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

     

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

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

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

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

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

     

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

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

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

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

  • @이니스프리
    2019.8.6 22:25

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

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

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

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

     

    그럼,

    Good night :)

  • @Hanam09
    이니스프리글쓴이
    2019.8.7 00:30

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

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

     

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

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

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

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

     

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

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

댓글 달기

번호 제목 글쓴이 날짜 조회 수
공지 사이트 정보 등록에 경고로 변경된 분들 참고하세요.7 마스터 2019.06.19 1093
공지 [중요 공지 포함] 사이트가 복구 되었습니다.42 마스터 2019.06.18 1399
공지 호스팅 신청이 다시 가능합니다.18 마스터 2019.04.18 2178
공지 회원 전용 페이지가 생겼습니다.15 마스터 2018.03.20 8036
공지 [필독] 질문하는 방법6 마스터 2018.02.23 1067
609 Beautifulsoup 로그인 문제1 Hanam09 10시간 전 30
608 부트스트랩 모달의 사이즈를 구해서 사이즈에 맞게 이모지 n개/행을 출력하려고 하는데요.5 이니스프리 14시간 전 44
607 Jquery 사용 시 $(window).load( function() 를 사용하시나요?5 로우지 2019.08.20 40
606 ftp.studyforus.com은 서비스를 중지하신건가요?2 Piedots 2019.08.19 33
605 출첵을 했는데 처리가 제대로 안됐어요! title: 머그잔(명탐정 피카츄)왕뚠뚠돈까스라이츄 2019.08.19 20
604 imgur 사용하시는분.... 잘아시는분...질문!5 홀민 2019.08.18 41
603 a태그를 특정 조건에서만 비활성화시키고 싶은데... +사소한 기능 문제6 title: 머그잔(명탐정 피카츄)왕뚠뚠돈까스라이츄 2019.08.08 81
602 이거 도메인 사용 가능한 거 맞죠?13 title: 머그잔(명탐정 피카츄)왕뚠뚠돈까스라이츄 2019.08.08 80
601 자바스크립트는 왜 이럴까요...5 title: 머그잔(명탐정 피카츄)왕뚠뚠돈까스라이츄 2019.08.07 51
600 창 크기에 따라서 표의 크기가 달라지는 태그는 없나요?6 title: 머그잔(명탐정 피카츄)왕뚠뚠돈까스라이츄 2019.08.05 71
자바스크립트 FormData와 관련된 메서드의 IE 호환성과 관련하여 질문 드립니다 ^^6 이니스프리 2019.08.05 81
598 프로알라 에디터를 사용하면 클라이언트단에서 이미지 리사이징을 할 수 있는가요?6 이니스프리 2019.08.02 67
597 IE11에서 이미지가 깨지는 현상을 해결할 수 있을까요?2 이니스프리 2019.08.02 79
596 야후재팬 메일 이용하시는분 계신가요?2 슬기 2019.07.29 46
595 Composer 설치 가능한 웹호스팅 추천 부탁드립니다4 이니스프리 2019.07.29 48
594 싸이월드 미니홈피 크롤링 관련하여 질문 드립니다 ^^3 이니스프리 2019.07.27 60
593 영상 다운로드 받는 방법이 어디없을까요?6 슬기 2019.07.25 78
592 출석 오류4 Piedots 2019.07.25 49
591 [그누보드] 테스트 메일 보내기가 안되요.6 연우빠 2019.07.23 72
590 사이트 로고 디자인을 의뢰하기 전에 질문 드려요 ^^ 이니스프리 2019.07.21 39
이전 1 2 3 4 5 6 7 8 9 10... 31다음
첨부 (1)
image 20190805141044.png
63.1KB / Download 1
서버에 요청 중입니다. 잠시만 기다려 주십시오...