재우니의 블로그

File APIs 를 이용하여 자바스크립트로 파일들을 읽어보기



HTML5는 마침내 File API 사양을 통해 로컬 파일과 상호 작용하는 표준 방법을 제공합니다 . 기능의 예로 File API를 사용하여 이미지가 서버로 전송되는 동안 미리보기 이미지 미리보기를 만들거나 사용자가 오프라인 상태 일 때 앱이 파일 참조를 저장할 수 있습니다. 또한 클라이언트 측 로직을 사용하여 업로드의 MIME 유형이 파일 확장자와 일치하는지 확인하거나 업로드 크기를 제한 할 수 있습니다.

이 스펙은 '로컬'파일 시스템에서 파일에 액세스하기위한 몇 가지 인터페이스를 제공합니다.

  1. File- 개별 파일; 이름, 파일 크기, MIME 형식 및 파일 핸들에 대한 참조와 같은 읽기 전용 정보를 제공합니다.
  2. FileList- 배열과 같은 일련의 File객체. ( <input type="file" multiple>데스크탑에서 파일 디렉토리를 생각 하거나 드래그).
  3. Blob - 파일을 바이트 범위로 분할 할 수 있습니다.

위의 데이터 구조와 함께 FileReader사용하면 익숙한 JavaScript 이벤트 처리를 통해 비동기 적으로 파일을 읽을 수 있습니다. 따라서 읽기 진행률을 모니터링하고 오류를 catch하고로드가 완료된시기를 판별하는 것이 가능합니다. 여러면에서 API는 XMLHttpRequest이벤트 모델 과 유사 합니다.


Selecting files

가장 먼저 할 일은 브라우저가 File API를 완벽하게 지원하는지 확인하는 것입니다.

// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
  // Great success! All the File APIs are supported.
} else {
  alert('The File APIs are not fully supported in this browser.');
}

물론 앱에서 이러한 API 중 일부만 사용하는 경우  snippet 을 수정하세요.

양식 입력을 사용하여 선택하기

파일을로드하는 가장 간단한 방법은 표준 <input type="file">요소 를 사용하는 것 입니다. JavaScript는 선택한 File객체 의 목록을 FileList 로 반환합니다. 한 번에 여러 파일을 선택할 수 있도록 'multiple'속성을 사용하는 예는 다음과 같습니다.



<input
type="file" id="files" name="files[]" multiple /> <output id="list"></output> <script> function handleFileSelect(evt) { var files = evt.target.files; // FileList object // files is a FileList of File objects. List some properties. var output = []; for (var i = 0, f; f = files[i]; i++) { output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ', f.size, ' bytes, last modified: ', f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a', '</li>'); } document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>'; } document.getElementById('files').addEventListener('change', handleFileSelect, false); </script>


끌어서 놓기를 사용하여 선택하기

파일을 로드 하는 또 다른 기술은 데스크탑에서 브라우저로 기본 드래그 앤 드롭입니다. 드래그 앤 드롭 지원을 포함하도록 앞의 ​​예제를 약간 수정할 수 있습니다.



<div
id="drop_zone">Drop files here</div> <output id="list"></output> <script> function handleFileSelect(evt) { evt.stopPropagation(); evt.preventDefault(); var files = evt.dataTransfer.files; // FileList object. // files is a FileList of File objects. List some properties. var output = []; for (var i = 0, f; f = files[i]; i++) { output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ', f.size, ' bytes, last modified: ', f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a', '</li>'); } document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>'; } function handleDragOver(evt) { evt.stopPropagation(); evt.preventDefault(); evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy. } // Setup the dnd listeners. var dropZone = document.getElementById('drop_zone'); dropZone.addEventListener('dragover', handleDragOver, false); dropZone.addEventListener('drop', handleFileSelect, false); </script>

참고 : 일부 브라우저는 <input type="file">요소를 기본 드롭 대상으로 처리 합니다. 앞의 예제에서 파일을 입력 필드로 드래그 해 보십시오.

파일 읽기

이제 재미있는 부분이 있습니다!

File레퍼런스를 얻은 후에 FileReader객체를 인스턴스화하여 내용을 메모리로 읽어들입니다. 로드가 끝나면 reader 의 onload이벤트가 시작되고 해당 result속성을 사용하여 파일 데이터에 액세스 할 수 있습니다.

FileReader 비동기 적으로 파일을 읽는 네 가지 옵션이 있습니다.

  • FileReader.readAsBinaryString(Blob|File)result속성에는 파일 / blob의 데이터가 바이너리 문자열로 포함됩니다. 모든 바이트는 [0..255] 범위의 정수로 표시됩니다.
  • FileReader.readAsText(Blob|File, opt_encoding)result속성은 파일 / blob의 데이터를 텍스트 문자열로 포함합니다. 기본적으로 문자열은 'UTF-8'로 디코딩됩니다. 선택적 인코딩 매개 변수를 사용하여 다른 형식을 지정할 수 있습니다.
  • FileReader.readAsDataURL(Blob|File)result속성에는 데이터 URL로 인코딩 된 파일 / blob의 데이터가 포함됩니다 .
  • FileReader.readAsArrayBuffer(Blob|File)result속성에는 파일 / blob의 데이터가 ArrayBuffer 객체로 포함됩니다.

이러한 읽기 방법 중 하나를 한 번에 호출 FileReader객체는 onloadstartonprogressonloadonabortonerror, 및 onloadend 진행 상황을 추적 할 수 있습니다.

아래의 예는, 사용자의 선택의 이미지를 필터링 호출 reader.readAsDataURL() 파일에, 데이터 URL에 'SRC'속성을 설정하여 미리보기를 렌더링합니다.


<style>
.thumb { height: 75px; border: 1px solid #000; margin: 10px 5px 0 0; } </style> <input type="file" id="files" name="files[]" multiple /> <output id="list"></output> <script> function handleFileSelect(evt) { var files = evt.target.files; // FileList object // Loop through the FileList and render image files as thumbnails. for (var i = 0, f; f = files[i]; i++) { // Only process image files. if (!f.type.match('image.*')) { continue; } var reader = new FileReader(); // Closure to capture the file information. reader.onload = (function(theFile) { return function(e) { // Render thumbnail. var span = document.createElement('span'); span.innerHTML = ['<img class="thumb" src="', e.target.result, '" title="', escape(theFile.name), '"/>'].join(''); document.getElementById('list').insertBefore(span, null); }; })(f); // Read in the image file as a data URL. reader.readAsDataURL(f); } } document.getElementById('files').addEventListener('change', handleFileSelect, false); </script>

파일 자르기

어떤 경우에는 전체 파일을 메모리로 읽어들이는 것이 최선의 방법은 아닙니다. 예를 들어 비동기 파일 업 로더를 작성하려고한다고 가정 해보십시오. 업로드 속도를 높이는 한 가지 방법은 별도의 바이트 범위 chunk 로 파일을 읽고 보내는 것입니다. 그런 다음 서버 구성 요소는 올바른 순서로 파일 내용을 재구성해야합니다.

운 좋게도 이 File인터페이스는 이 사용 사례를 지원하는 슬라이스 메서드를 지원합니다. 이 메서드는 시작 바이트를 첫 번째 인수로 사용하고 바이트를 두 번째로 끝내고 옵션 콘텐츠 형식 문자열을 세 번째 인수로 사용합니다.

var blob = file.slice(startingByte, endindByte);
reader.readAsBinaryString(blob);

다음 예제에서는 하나의 파일의 chunk 읽기를 보여줍니다.  주목할 부분은  onloadend 사용하는 것과,  onload 이벤트를 사용하지 않고  evt.target.readyState 를 체크 한다는 것입니다.


<style>
  #byte_content {
    margin: 5px 0;
    max-height: 100px;
    overflow-y: auto;
    overflow-x: hidden;
  }
  #byte_range { margin-top: 5px; }
</style>

<input type="file" id="files" name="file" /> Read bytes: 
<span class="readBytesButtons">
  <button data-startbyte="0" data-endbyte="4">1-5</button>
  <button data-startbyte="5" data-endbyte="14">6-15</button>
  <button data-startbyte="6" data-endbyte="7">7-8</button>
  <button>entire file</button>
</span>
<div id="byte_range"></div>
<div id="byte_content"></div>

<script>
  function readBlob(opt_startByte, opt_stopByte) {

    var files = document.getElementById('files').files;
    if (!files.length) {
      alert('Please select a file!');
      return;
    }

    var file = files[0];
    var start = parseInt(opt_startByte) || 0;
    var stop = parseInt(opt_stopByte) || file.size - 1;

    var reader = new FileReader();

    // If we use onloadend, we need to check the readyState.
    reader.onloadend = function(evt) {
      if (evt.target.readyState == FileReader.DONE) { // DONE == 2
        document.getElementById('byte_content').textContent = evt.target.result;
        document.getElementById('byte_range').textContent = 
            ['Read bytes: ', start + 1, ' - ', stop + 1,
             ' of ', file.size, ' byte file'].join('');
      }
    };

    var blob = file.slice(start, stop + 1);
    reader.readAsBinaryString(blob);
  }
  
  document.querySelector('.readBytesButtons').addEventListener('click', function(evt) {
    if (evt.target.tagName.toLowerCase() == 'button') {
      var startByte = evt.target.getAttribute('data-startbyte');
      var endByte = evt.target.getAttribute('data-endbyte');
      readBlob(startByte, endByte);
    }
  }, false);
</script>

읽기 진행 상황 모니터링

비동기 이벤트 처리를 사용할 때 우리가 무료로 얻을 수있는 좋은 점 중 하나는 파일 읽기의 진행 상황을 모니터하는 기능입니다. 대용량 파일에 유용하고, 오류를 포착하고, 읽기가 완료된시기를 파악할 수 있습니다.

the onloadstart및 onprogressevents는 읽기 진행을 모니터하는 데 사용할 수 있습니다.

아래 예제는 진행 상태 막대를 표시하여 읽기 상태를 모니터링하는 방법을 보여줍니다. 작동중인 진행률 표시기를 보려면 큰 파일이나 원격 드라이브에서 파일을 시도하십시오.


<style>
  #progress_bar {
    margin: 10px 0;
    padding: 3px;
    border: 1px solid #000;
    font-size: 14px;
    clear: both;
    opacity: 0;
    -moz-transition: opacity 1s linear;
    -o-transition: opacity 1s linear;
    -webkit-transition: opacity 1s linear;
  }
  #progress_bar.loading {
    opacity: 1.0;
  }
  #progress_bar .percent {
    background-color: #99ccff;
    height: auto;
    width: 0;
  }
</style>

<input type="file" id="files" name="file" />
<button onclick="abortRead();">Cancel read</button>
<div id="progress_bar"><div class="percent">0%</div></div>

<script>
  var reader;
  var progress = document.querySelector('.percent');

  function abortRead() {
    reader.abort();
  }

  function errorHandler(evt) {
    switch(evt.target.error.code) {
      case evt.target.error.NOT_FOUND_ERR:
        alert('File Not Found!');
        break;
      case evt.target.error.NOT_READABLE_ERR:
        alert('File is not readable');
        break;
      case evt.target.error.ABORT_ERR:
        break; // noop
      default:
        alert('An error occurred reading this file.');
    };
  }

  function updateProgress(evt) {
    // evt is an ProgressEvent.
    if (evt.lengthComputable) {
      var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
      // Increase the progress bar length.
      if (percentLoaded < 100) {
        progress.style.width = percentLoaded + '%';
        progress.textContent = percentLoaded + '%';
      }
    }
  }

  function handleFileSelect(evt) {
    // Reset progress indicator on new file selection.
    progress.style.width = '0%';
    progress.textContent = '0%';

    reader = new FileReader();
    reader.onerror = errorHandler;
    reader.onprogress = updateProgress;
    reader.onabort = function(e) {
      alert('File read cancelled');
    };
    reader.onloadstart = function(e) {
      document.getElementById('progress_bar').className = 'loading';
    };
    reader.onload = function(e) {
      // Ensure that the progress bar displays 100% at the end.
      progress.style.width = '100%';
      progress.textContent = '100%';
      setTimeout("document.getElementById('progress_bar').className='';", 2000);
    }

    // Read in the image file as a binary string.
    reader.readAsBinaryString(evt.target.files[0]);
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>

발췌 : https://www.html5rocks.com/en/tutorials/file/dndfiles/