File System API
참고 : 이 기능은 Web Worker에서 사용할 수 있습니다.
File System API는 파일을 읽고, 쓰고, 관리하는 기능을 제공합니다. File System Access API 명세에서 추가하는 기능을 통해 기기의 파일 시스템에 직접 접근할 수 있습니다.
개념과 사용법
File System API를 사용하여 사용자의 로컬 기기 또는 사용자가 접근 가능한 네트워크 파일 시스템의 파일과 상호작용할 수 있습니다. 이 API의 핵심 기능은 파일 읽기, 쓰기 또는 저장, 디렉터리 구조에 대한 접근입니다.
파일 및 디렉터리와의 상호작용 대부분은 핸들을 통해 진행합니다. 부모 FileSystemHandle
클래스를 기반으로, 각각 파일과 디렉터리 핸들을 정의하는 자식 클래스인 FileSystemFileHandle
과 FileSystemDirectoryHandle
클래스가 존재합니다.
핸들은 사용자 시스템의 어느 파일이나 디렉터리를 나타냅니다. 핸들에 접근하려면 우선 window.showOpenFilePicker()
나 window.showDirectoryPicker()
등의 메서드를 호출해 사용자에게 파일 또는 디렉터리 선택창을 보여줘야 합니다. 사용자가 선택창을 통해 파일 또는 디렉터리를 성공적으로 선택하면 핸들이 반환됩니다.
다음 방법으로도 핸들을 얻을 수 있습니다.
각각의 핸들 종류는 자신만의 기능을 가지고 있으며, 서로 약간의 차이가 존재합니다(인터페이스에서 자세한 내용을 확인하세요). 이렇게 핸들을 얻은 후에는 파일 데이터에 접근하거나, 선택한 디렉터리의 내용물을 포함한 정보에 접근할 수 있습니다. File System API는 웹이 그동안 부족했던 파일 관련 기능을 제공합니다. 그러나 이 API의 설계에서 제일 중요한 부분은 보안이었고, 파일과 디렉터리로 데이터로의 접근은 사용자가 명시적으로 허용하지 않는 한 불가능합니다(출처 전용 파일 시스템은 일반 파일 시스템과 달리 사용자가 볼 수 없으므로 예외).
참고 :이 API의 구성 기능을 사용할 때 발생할 수 있는 예외들은 명세에 정의된 것과 동일하게 각 기능의 문서에 나열돼있습니다. 그러나 API와 운영체제 사이의 상호작용 중 발생할 수 있는 예외때문에 실제로는 상황이 좀 더 복잡합니다.오류 매핑을 명세에 추가하자는 제안이 올라온 상태며, 이 제안에서도 오류에 대한 유용한 추가 정보를 볼 수 있습니다.
참고 :FileSystemHandle
기반 객체는 IndexedDB 데이터베이스 인스턴스로 직렬화할 수 있고, postMessage()
로 전송할 수도 있습니다.
출처 전용 파일 시스템
출처 전용 파일 시스템(Origin Private File System, OPFS)은 File System API가 제공하는 저장소 엔드포인트로, 일반 파일 시스템과 달리 사용자는 볼 수 없으며 페이지의 출처에서만 접근할 수 있습니다. OPFS는 콘텐츠의 직접 쓰기 접근을 허용하는, 고도로 성능 최적화된 특별한 유형의 파일을 제공합니다.
출처 전용 파일 시스템 문서에서 사용법을 알아보세요.
파일 저장하기
- 비동기 핸들에서는
FileSystemWritableFileStream
인터페이스를 사용하세요. 저장할 데이터를Blob
,String
객체, 문자열 리터럴, 또는ArrayBuffer
로 만든 후, 스트림을 열어 데이터를 파일에 저장할 수 있습니다. 대상 파일은 새로운 파일일 수도 있고 기존 파일일 수도 있습니다. - 동기 핸들인
FileSystemSyncAccessHandle
의 경우write()
메서드를 사용해 변경점들을 파일에 쓸 수 있습니다. 선택적으로, 특정 시점에 변경점들을 디스크에 작성해야 한다면flush()
를 호출할 수 있습니다. (호출하지 않으면 운영체제가 적당한 시점에 처리하며 대부분의 경우 이것으로도 충분합니다)
인터페이스
FileSystemHandle
-
파일 또는 디렉터리 항목을 나타내는 객체입니다. 다수의 핸들이 같은 항목을 가리킬 수 있습니다. 대부분의 경우
FileSystemHandle
을 직접 다루지 않고 자식 인터페이스인FileSystemFileHandle
과FileSystemDirectoryHandle
을 사용합니다. FileSystemFileHandle
-
파일 시스템 항목에 대한 핸들을 제공합니다.
FileSystemDirectoryHandle
-
파일 시스템 디렉터리에 대한 핸들을 제공합니다.
FileSystemSyncAccessHandle
-
파일 시스템 항목에 대한 동기적 핸들을 제공합니다. 단일 파일의 데이터를 디스크에 직접 쓸 수 있는 핸들입니다. 파일 읽기와 쓰기의 동기적 작동 방식을 활용하면 비동기적 작업이 높은 오버헤드를 유발하는 WebAssembly 등의 컨텍스트에서 고성능을 유지할 수 있습니다. 이 클래스는 전용 웹 워커에서만, 그리고 출처 전용 파일 시스템 내의 파일에 대해서만 사용할 수 있습니다.
FileSystemWritableFileStream
-
WritableStream
객체에 편의 메서드를 추가한 인터페이스로, 디스크의 단일 파일에서 동작합니다.
다른 인터페이스 확장
Window.showDirectoryPicker()
-
사용자가 디렉터리를 선택할 수 있는 디렉터리 선택창을 엽니다.
Window.showOpenFilePicker()
-
사용자가 파일 한 개 또는 여러 개를 선택할 수 있는 파일 선택창을 엽니다.
Window.showSaveFilePicker()
-
사용자가 파일을 저장할 수 있는 파일 선택창을 엽니다.
DataTransferItem.getAsFileSystemHandle()
-
드래그된 아이템이 파일이라면
FileSystemFileHandle
을, 디렉터리라면FileSystemDirectoryHandle
을 반환합니다. StorageManager.getDirectory()
-
출처 전용 파일 시스템의 디렉터리와 콘텐츠에 접근할 때 사용하는
FileSystemDirectoryHandle
객체의 참조를 가져올 때 사용합니다.FileSystemDirectoryHandle
객체로 이행하는Promise
를 반환합니다.
예제
파일 접근하기
다음 코드는 사용자가 파일을 선택할 수 있는 파일 선택창을 엽니다.
async function getFile() {
// 파일 선택창을 열고 구조 분해로 첫 번째 핸들을 가져옴
const [fileHandle] = await window.showOpenFilePicker();
const file = await fileHandle.getFile();
return file;
}
다음 비동기 함수는 파일 선택창을 열고, 사용자가 파일을 선택하면 getFile()
메서드를 사용해 그 파일의 내용을 가져옵니다.
const pickerOpts = {
types: [
{
description: "Images",
accept: {
"image/*": [".png", ".gif", ".jpeg", ".jpg"],
},
},
],
excludeAcceptAllOption: true,
multiple: false,
};
async function getTheFile() {
// 파일 선택창을 열고 구조 분해로 첫 번째 핸들을 가져옴
const [fileHandle] = await window.showOpenFilePicker(pickerOpts);
// 파일 내용 가져오기
const fileData = await fileHandle.getFile();
}
디렉터리 접근하기
다음 예제에서는 주어진 이름을 가진 디렉터리의 핸들을 가져옵니다. 주어진 이름의 디렉터리가 존재하지 않으면 생성됩니다.
const dirName = "directoryToGetName";
// 'currentDirHandle'이라는 이름의 디렉터리 핸들이 이미 존재한다고 가정
const subDir = currentDirHandle.getDirectoryHandle(dirName, { create: true });
다음 비동기 함수는 주어진 디렉터리 핸들을 기준으로, resolve()
를 사용해 사용자가 선택한 파일의 상대 경로를 찾습니다.
async function returnPathDirectories(directoryHandle) {
// 파일 선택창을 열어 파일 핸들 가져오기
const [handle] = await self.showOpenFilePicker();
if (!handle) {
// 사용자가 취소했거나 다른 이유로 파일 열기 실패
return;
}
// 위의 파일 핸들이 주어진 디렉터리 핸들 내에 위치하는지 확인
const relativePaths = await directoryHandle.resolve(handle);
if (relativePaths === null) {
// 디렉터리 핸들 밖에 있음
} else {
// relativePaths는 상대 경로를 구성하는 경로 이름의 배열임
for (const name of relativePaths) {
// 각 항목 기록
console.log(name);
}
}
}
파일 쓰기
다음 비동기 함수는 저장 파일 선택창을 엽니다. 저장 파일 선택창은 파일이 선택된 후 FileSystemFileHandle
을 반환합니다. 그 후 FileSystemFileHandle.createWritable()
메서드로 쓰기 스트림을 생성할 수 있습니다.
다음으로, 스트림에 사용자의 Blob
을 쓰고, 스트림을 닫습니다.
async function saveFile() {
// 새 핸들 생성
const newHandle = await window.showSaveFilePicker();
// Blob을 쓸 FileSystemWritableFileStream 생성
const writableStream = await newHandle.createWritable();
// 파일에 쓰기
await writableStream.write(imgBlob);
// 파일을 닫아서 콘텐츠 쓰기가 디스크에 반영되도록 하기
await writableStream.close();
}
다음 코드는 write()
메서드에 전달할 수 있는 다양한 옵션을 보입니다.
// 옵션 없이 데이터만
writableStream.write(data);
// 스트림에 쓸 때 사전에 결정한 위치부터 시작
writableStream.write({ type: "write", position, data });
// 현재 파일 커서를 지정된 위치로 변경
writableStream.write({ type: "seek", position });
// 파일이 size 바이트 크기가 되도록 변경
writableStream.write({ type: "truncate", size });
동기적으로 OPFS 파일 읽고 쓰기
이 예제는 출처 전용 파일 시스템에서 파일을 동기적으로 읽고 쓰는 방법을 보입니다.
다음은 웹 워커 내의 비동기 이벤트 처리기 함수입니다. 메인 스레드로부터 메시지를 받으면
- 동기적 파일 접근 핸들을 생성합니다.
- 파일의 크기를 가져와서, 내용을 담을
ArrayBuffer
를 생성합니다. - 파일 내용을 읽어 버퍼에 넣습니다.
- 메시지를 인코딩 후 파일 끝에 붙입니다.
- 디스크에 변경점 반영 후 접근 핸들을 닫습니다.
onmessage = async (e) => {
// 메인 스크립트에서 받은 메시지 회수
const message = e.data;
// OPFS 파일 작성을 위한 핸들 가져오기
const root = await navigator.storage.getDirectory();
const draftHandle = await root.getFileHandle("draft.txt", { create: true });
// 동기적 접근 핸들 가져오기
const accessHandle = await draftHandle.createSyncAccessHandle();
// 파일 크기 가져오기
const fileSize = accessHandle.getSize();
// 파일 내용을 버퍼에 넣기
const buffer = new DataView(new ArrayBuffer(fileSize));
const readBuffer = accessHandle.read(buffer, { at: 0 });
// 메시지를 파일 끝에 붙이기
const encoder = new TextEncoder();
const encodedMessage = encoder.encode(message);
const writeBuffer = accessHandle.write(encodedMessage, { at: readBuffer });
// 디스크에 반영
accessHandle.flush();
// 작업 종료 후엔 항상 FileSystemSyncAccessHandle 닫아주기
accessHandle.close();
};
참고 :초기 명세에서는 close()
, flush()
, getSize()
, truncate()
가 비동기 메서드로 정의돼 불편했습니다.지금은 수정됐지만, 일부 브라우저에서는 아직 비동기 메서드로서 제공됩니다.
명세서
Specification |
---|
File System Standard |
File System Access |
브라우저 호환성
api.FileSystemHandle
BCD tables only load in the browser
api.FileSystemFileHandle
BCD tables only load in the browser
api.FileSystemDirectoryHandle
BCD tables only load in the browser
api.FileSystemWritableFileStream
BCD tables only load in the browser
api.FileSystemSyncAccessHandle
BCD tables only load in the browser