Firefox 3.6의 W3C FileAPI

웹 애플리케이션은 서버로 파일을 업로드 하는 경우처럼 필요한 경우 사용자에게 파일을 선택하도록 합니다. 웹 애플리케이션이 플러그인을 사용하지 않는 다면<input type="file"/>같은 HTML input 엘리먼트를 통해 파일을 선택하게 됩니다. Firefox 3.6은 이제 W3C File API의 많은 부분을 지원합니다. W3C File API는 선택된 파일들을 메모리로 비동기적으로 읽어들이는 기능과 웹 애플리케이션 내에서 파일 데이터를 다룰 수 있는 방법 – 예를 들어 클라이언트 사이드에서 이미지를 업로드 전에 미리 보기를 한다거나 MP3 파일 내의 ID3 태그를 살펴본다던가 JPEG 파일의 EXIF 데이터를 살펴 보는 것 – 을 정의하고 있습니다. 이 API는 새로운 것이며 Firefox 3에서 소개됐던 파일 API를 대체합니다.

W3C File API 초안(2009년 11월에 워킹 드래프트가 됨)이 나오기 전에 이미 Firefox 3와 그 이후 버전들은 파일들을 메모리에 동기적으로으로 읽어들일 수 있는 기능을 제공했습니다. 하지만 Firefox 3.6에서 구현된 비동기적 파일 API에 대한 지지로 동기적으로 읽어들이는 기능을 더 이상 지원하지 않을 것이라는 것을 아는 것은 중요합니다. 지원이 중단된 API를 사용하면 파일에 동기적으로 접근할 수 있습니다.

// 파일에 대한 제어(handle)를 얻은 다음에
// 파일 데이터에 접근
var dataURL = file.getAsDataURL();
img.src = dataURL;

메인 쓰레드에서 동기적으로 파일을 읽어들이기 때문에 지원이 중단될(deprecated) 것으로 간주되지만, Firefox 3.6은 위와 같은 코드를 사용하는 것은 계속해서 지원할 것입니다. 큰 파일의 경우 이 API는 파일을 읽어들이는 일로 인해 다른 일에 대한 원하지 않는 지연 요소가 될 수 있습니다. 게다가, 파일 객체 자체는 분리된 읽기 객체를 가진 것이 아니라 그 자체에서 읽어들이는 메소드를 제공하고 있습니다. 이러한 문제에 대한 검토는 새로운 Firefox 3.6 파일 API(그리고 그 명세)에 기술적으로 직접 반영됐습니다. 이 글의 나머지 부분은 새로 소개하는 FileAPI에 대한 부분입니다.

선택된 파일에 접근

Firefox 3.6은 input 엘리먼트에서 다중 파일 선택을 지원하고 FileList 인터페이스를 사용하여 선택된 파일 모두를 반환합니다. 이전 버전의 Firefox는 input 엘리먼트를 통하여 단지 하나의 파일을 선택할 수 있는 것만을 지원했습니다. 부가적으로, FileList 인터페이스는 HTML5 드래그 앤 드롭 APIDataTransfer 인터페이스의 한 속성이 됐습니다. 그래서, 사용자는 웹 페이지 내에서 여러 파일을 끌어다 특정 영역에 놓을 수 있게 됐습니다.

다음의 HTML은 다중 파일을 선택할 수 있는 표준 파일 선택기를 표시합니다.

<input id="inputFiles" type="file" multiple="" />

multiple 속성을 사용하지 않으면 단지 하나의 파일만 선택할 수 있다는 걸 기억해야 합니다.

input엘리먼트를 통해서나 또는 DataTransfer 객체를 통해서 얻은 선택된 파일들에 대해서 FileList를 통해 하나 하나 열거하며 원하는 작업을 할 수 있습니다.

var files = document.getElementById("inputFiles").files;

 
// 또는 드래그 이벤트 e일 경우
// var dt = e.dataTransfer; var files = dt.files
 
for (var i = 0; i < files.length; i++) {

  var file = files[i];
  handleFile(file);
 
}

파일 객체의 속성들

일단 FileList를 통해 선택된 개별 파일들의 참조를 얻으면, name, type, size 속성을 가진 File 객체를 얻을 수 있습니다. 위에 코드에 이어지는 부분을 계속 살펴보겠습니다.

function handleFile(file) {
    // JPEG MIME 타입을 위한 RegExp
    var imageType = /image\/jpeg/;

 
    // 일치하는지 확인
    if (!file.type.match(imageType)) {
        return false;

    }
   // 이미지 크기가 한계치를 넘어서는지 확인
   if(file.size > maxSize) {
      alert("Choose a smaller photo!");

      return false;
   }
  // 파일 이름을 이미지에 추가
  var picData = document.createTextNode(file.name);

  dataGrid.appendChild(picData);
  return true;
}

size 속성은 파일의 바이트 크기입니다. name 속성은 경로 정보가 없는 파일 이름입니다. type 속성은 RFC2046 MIME 타입으로 표현된 파일의 미디어 유형을 나타내는 소문자 아스키 문자열입니다. type 속성은 특히 위의 예제와 같이 특정 파일인지를 스크립트 상에서 알아야 할 때 유용합니다. Firefox 3.6이 파일 type을 결정하지 못하는 경우 빈 문자열이 반환됩니다.

파일 읽기

Firefox 3.6과 그 이후 버전들은 메모리로 파일 데이터를 비동기적으로 읽어들이고 이를 처리하기 위해 이벤트 콜백 함수를 사용하는 FileReader 객체를 지원합니다. 이 객체는 다음과 같은 표준적인 방법으로 인스턴스화 됩니다.

var binaryReader = new FileReader();

이벤트 처리 속성들은 파일을 읽는 작업에 대한 result 값에 따른 작업을 할 때 사용됩니다. 매우 큰 파일에 대해서 파일이 메모리에 읽어들여지고 있는 상황을 관찰하는 것이 가능합니다(이벤트 처리 함수를 설정하기 위해 onprogress 이벤트 처리기 속성을 사용합니다). 이러한 기능은 드라이브가 로컬에 있는지 확실치 않은 상황이나 파일이 얼마나 큰지 알 수 없는 상황에서 매우 유용합니다.

FileReader 객체는 메모리로 파일을 읽어 들이는 세가지 메소드를 지원합니다. 각각의 메소드는 프로그램적으로 다른 형식으로 파일 데이터에 접근하게 해줍니다. 다만 실질적으로 하나의 FileReader 객체에는 한가지 종류의 읽기 메소드를 만을 호출해야 합니다.

  • filereader.readAsBinaryString(file): 각각의 바이트를 0 ~ 255 범위의 정수 값으로 표현하는 바이너리 문자열을 비동기적으로 반환합니다. 이는 MP3 파일의 ID3 태그를 살펴보거나 JPEG 파일의 EXIF 데이터 같은 파일 데이터의 바이너리 값을 다룰 때 유용합니다.
  • filereader.readAsText(file, encoding): encoding 매개변수에 주어진 형식(예, encoding = “UTF-8”)에 따라 문자열을 비동기적으로 반환합니다. 이는 XML 파일을 파싱하는 것 같은 텍스트 파일을 다룰 때 유용합니다.
  • filereader.readAsDataURL(file): 비동기 적으로 Data URL을 반환합니다. Firefox 3.6은 큰 URL들을 허용합니다. 그래서 이러한 기능은 URL이 웹 페이지 상에 이미지, 비디오, 오디오 데이터 같은 미디어 콘텐트를 나타내는 것을 도울 때 특히 유용합니다.

다음은 위의 기능들 모두를 한 번에 이해하는 도움이 되는 예제입니다.

if (files.length > 0) {

    if (!handleFile(files[0])) {
        invalid.style.visibility="visible";

        invalid.msg = "Select a JPEG Image";
     }
}
 
var binaryReader = new FileReader();

binaryReader.onload = function(){
   var exif = findEXIFInJPG(binaryReader.result);

   if (!exif) {
      // 데이터 없는 경우의 처리
   }
   else {
    // exif 데이터 표시

   }
 
binaryReader.onprogress = updateProgress;
binaryReader.onerror = errorHandler;

 
binaryReader.readAsBinaryString(file);
 
function updateProgress(evt){
   // PorgressEvent의 lengthComputable, loaded, total 사용
   if (evt.lengthComputable) {

          var loaded = (evt.loaded / evt.total);
          if (loaded < 1) {

            // 프로그레스바 갱신
            progMeter.style.width = (loaded * 200) + "px";

          }
   }
}
 
function errorHandler(evt) {
  if(evt.target.error.code == evt.target.error.NOT_FOUND_ERR) {

   alert("File Not Found!");
  }
}

바이너리 데이터를 다루기 위해서 charCodeAt 함수를 사용하면 유용합니다. 예를 들어 다음과 같이 하면 주어진 위치(index)의 문자의 유니코드 값을 얻을 수 있습니다.

function getByteAt(file, idx) {

    return file.charCodeAt(idx);
}

Firefox 3.6에서 동작하는 이와 유사한 예제를 폴 루제(Paul Rouget)의 File API 고급 데모에 찾을 수 있습니다. 이 예제에는 이미지를 표시하기 위해 readAsDataURL 메소드를 사용하는 것을 포함하여 JPEG 파일의 EXIF 정보를 찾기 위해 바이너리를 분석(readAsBinaryString 사용)하는 방법까지 포함돼 있습니다.

FileAPI 명세와 관련된 부언

the File API는 W3C의 워킹 드래프트(public working draft)에 있기 때문에 다른 브라우저들이 곧 구현할 것입니다. Firefox 3.6은 이 API를 꽤 온전하게 구현했습니다. 하지만 명세서에서 언급하는 일부 기술들은 누락돼 있는 것도 사실입니다. 특히, File 객체의 urn 기능은 아직 구현되지 않았고, slice 메소드를 사용하여 파일 데이터를 바이트 범위로 추출하는 기능도 구현되지 않았습니다. Worker Threads의 일부분으로 파일을 동기적으로 읽는 방법 또한 구현되지 않았습니다. 이러한 기능들은 차후에 나올 버전들에서 구현될 것입니다.

참고자료

원저자: Arun Ranganathan – 원문으로 가기

작성자: Kim, Myung Shin

Kim, Myung Shin가 작성한 문서들…


댓글이 없습니다.

댓글 쓰기