서버에서 생성 된 여러 파일을 js + ajax로 연속 다운로드 (Chrome)

업무 시스템은 엑셀을 좋아해요・・・



안녕하세요.
오늘도 오늘로서 업무 시스템 개발. 필자입니다.

갑작스럽지만 업무 시스템은 엑셀 좋아하네요. 라고 할까. 여러분 엑셀 좋아해요.
물론 CSV라든가를 생성해 결과를 돌려준다고 하는 움직임을 하는 일도 있습니다만, Rails측에서 유저의 입력치로부터 엑셀 파일에 캐릭터 라인을 입력해, 그 결과의 엑셀을 돌려준다. (CSV는 SQL의 실행 결과, 엑셀은 장표 등의 출력처럼 구분하여 사용합니다)

라고 하는 처리를 하는 일이 있습니다.
필자는 자주, Ruby로 엑셀을 편집·작성할 때에 RubyXl 라고 하는 gem을 이용하겠습니다만, 이번은 엑셀을 작성한 후의 이야기로,

어떻게 사용자가 생성한 엑셀 파일을 다운로드 하는가 하는 내용의 기사가 됩니다.

이번 전제 조건


  • 엑셀 파일은 사용자의 입력 값에 따라 서버 측에서 생성됩니다.
  • ajax를 이용한 비동기 통신이며, Rails의 send_file는 사용할 수 없다.
  • 엑셀은 복수개 생성될 가능성이 있고, 그것을 zip등으로 굳히지 않고 개별적으로 다운로드 시킬 필요가 있다
  • 브라우저는 Google 크롬
  • 엑셀 생성 후 저장처의 파일 패스를 ajax로 js측에 돌려주고, 그 패스에 액세스 해 다운로드 처리를 달릴 수 있는 흐름?

  • 라는 곳에서 해 보았습니다.

    실패 예



    우선은, 안 좋은 예로부터입니다.
    심플하게 「패스를 알고 있으니까, window.location.href = "ファイルパス" 생각하고 실패한 패턴.
    $.ajax({
            //ここでサーバーへリクエストを送信
          }).done(function(json) {
            //サーバーから帰ってきたファイルパス群(配列)を受け取って一回一回ダウンロードしようとして失敗
            var downloadUrls = json.path;
            downloadUrls.forEach(function (value, idx) {
                const response = {
                  file: value,
                };
                setTimeout(() => {
                    window.location.href = response.file;
                }, idx * 50)
            })
          }).fail(function(err){
              console.log('へへ・・・悪い・・・失敗しちまったよ');
          })
    

    실패라고 할까, Chorme측의 보안 설정? 에 오류 메시지가 표시됩니다.
    한 번 무리하게 해 window.location.href 로 다운로드시켜 두면, 잠시동작시킬 수는 있었습니다만, 원래 워닝 메세지 나오고 있고, 보안적으로 어떻다고 하는 이야기이므로 각하.

    성공 예 (일단)



    조사하면 조사할수록 window.location.href 그럼 어려울까라는 것을 알았습니다.
    (그렇지만 그런 호이 호이 용서하고 있으면, 바이러스라든지 간단하게 다운로드시켜 버리는 것 )

    아무래도, 여러가지 조사하고 있으면,
    chrome에서 파일을 다운로드시킬 때에는 donwload 속성이 되는 것을 부여한 a 태그로 다운로드 받을 수 있으면 좋다.

    이번은 다운로드 처리는 자동으로 실시하는 사양이므로, 유저님께 다운로드 버튼을 클릭시키는 등이라고 하는 것은 해 받을 수는 없기 때문에, 다음과 같이 대응했습니다.
    $.ajax({
         //ここでサーバー側にリクエストを送る
        }).done(function(json) {
          //サーバー側からもらったファイルパスを利用して、一時的に<a>タグを生成し、クリックイベントを起こした後、aタグを除去
          var downloadUrls = json.path;//['url1','url2']みたいに結果が帰ってきている
          downloadUrls.forEach(function (value, idx) {
              const response = {
                file_path: value,
              };
              setTimeout(() => {
                  //aタグを生成
                  const a = document.createElement('a');
                  //href属性にファイルパスを生成
                  a.href = response.file_path;
                  //download属性にファイル名を付与。
                  a.download = 'ファイル名';
                  //生成したaタグをクリックするイベント
                  a.click();
                  //用無しのaタグ君には消えてもらうかな!
                  document.body.removeChild(a);
              }, idx * 50)
          })
        }).fail(function(err){
            console.log('失敗じゃない。諦めたときが失敗なんだ。');
        })
    

    이 녀석, 움직일거야! 라는 일로 우선 기대한 동작을 해 주게 되었습니다.

    zip으로 굳어 돌려주는 것이 편하다고 생각했습니다만, 각하되었으므로 우선 이것으로 움직여 보았습니다.
    당연합니다만, 보안의 문제가 있으므로, 어떤 방법이 제일 좋은 것일까・・・라고 고민하는 날들입니다.

    좋은 웹페이지 즐겨찾기