Programing

HTML5는 폴더 또는 폴더 트리의 드래그 드롭 업로드를 허용합니까?

lottogame 2020. 11. 8. 09:10
반응형

HTML5는 폴더 또는 폴더 트리의 드래그 드롭 업로드를 허용합니까?


나는 이것을하는 어떤 예도 보지 못했습니다. API 사양에서 허용되지 않습니까?

사진의 전체 폴더 트리를 업로드하기위한 간편한 드래그 드롭 솔루션을 찾고 있습니다.


이제 Chrome> = 21 덕분에 가능합니다.

function traverseFileTree(item, path) {
  path = path || "";
  if (item.isFile) {
    // Get file
    item.file(function(file) {
      console.log("File:", path + file.name);
    });
  } else if (item.isDirectory) {
    // Get folder contents
    var dirReader = item.createReader();
    dirReader.readEntries(function(entries) {
      for (var i=0; i<entries.length; i++) {
        traverseFileTree(entries[i], path + item.name + "/");
      }
    });
  }
}

dropArea.addEventListener("drop", function(event) {
  event.preventDefault();

  var items = event.dataTransfer.items;
  for (var i=0; i<items.length; i++) {
    // webkitGetAsEntry is where the magic happens
    var item = items[i].webkitGetAsEntry();
    if (item) {
      traverseFileTree(item);
    }
  }
}, false);

자세한 정보 : https://protonet.info/blog/html5-experiment-drag-drop-of-folders/


불행히도 주어진 디렉토리에 대한 모든 (파일 또는 디렉토리) 항목을 readEntries반드시 반환하지는 않기 때문에 기존 답변 중 어느 것도 완전히 정확 하지 않습니다 . 이는 API 사양의 일부입니다 (아래 문서 섹션 참조).

실제로 모든 파일을 가져 오려면 readEntries빈 배열을 반환 할 때까지 (우리가 만나는 각 디렉토리에 대해) 반복적 으로 호출해야합니다 . 그렇지 않으면 Chrome과 같은 디렉토리의 일부 파일 / 하위 디렉토리가 누락 readEntries되고 한 번에 최대 100 개의 항목 만 반환됩니다.

Promises ( await/ async)를 사용하여 readEntries(비동기이므로) 올바른 사용법을보다 명확하게 보여주고 BFS를 사용하여 디렉토리 구조를 탐색합니다.

// Drop handler function to get all files
async function getAllFileEntries(dataTransferItemList) {
  let fileEntries = [];
  // Use BFS to traverse entire directory/file structure
  let queue = [];
  // Unfortunately dataTransferItemList is not iterable i.e. no forEach
  for (let i = 0; i < dataTransferItemList.length; i++) {
    queue.push(dataTransferItemList[i].webkitGetAsEntry());
  }
  while (queue.length > 0) {
    let entry = queue.shift();
    if (entry.isFile) {
      fileEntries.push(entry);
    } else if (entry.isDirectory) {
      queue.push(...await readAllDirectoryEntries(entry.createReader()));
    }
  }
  return fileEntries;
}

// Get all the entries (files or sub-directories) in a directory 
// by calling readEntries until it returns empty array
async function readAllDirectoryEntries(directoryReader) {
  let entries = [];
  let readEntries = await readEntriesPromise(directoryReader);
  while (readEntries.length > 0) {
    entries.push(...readEntries);
    readEntries = await readEntriesPromise(directoryReader);
  }
  return entries;
}

// Wrap readEntries in a promise to make working with readEntries easier
// readEntries will return only some of the entries in a directory
// e.g. Chrome returns at most 100 entries at a time
async function readEntriesPromise(directoryReader) {
  try {
    return await new Promise((resolve, reject) => {
      directoryReader.readEntries(resolve, reject);
    });
  } catch (err) {
    console.log(err);
  }
}

Codepen의 전체 작업 예제 : https://codepen.io/anon/pen/gBJrOP

FWIW 나는 받아 들인 대답을 사용할 때 40,000 개의 파일 (100 개가 넘는 파일 / 하위 디렉토리를 포함하는 많은 디렉토리)이 포함 된 디렉토리에서 예상 한 모든 파일을 가져 오지 않았기 때문에 이것을 선택했습니다.

선적 서류 비치:

이 동작은 FileSystemDirectoryReader에 문서화되어 있습니다. 강조가 추가 된 발췌 :

readEntries () 일부 디렉토리 항목을
포함하는 배열을 반환 합니다 . 배열의 각 항목은 FileSystemEntry (일반적으로 FileSystemFileEntry 또는 FileSystemDirectoryEntry)를 기반으로하는 개체입니다.

그러나 공정하게 말하면 MDN 문서는 다른 섹션에서 이것을 더 명확하게 할 수 있습니다. readEntries () 문서는 단순히 노트 :

readEntries () 메소드는 읽고있는 디렉토리 내의 디렉토리 항목을 검색하여 제공된 콜백 함수에 배열로 전달합니다.

그리고 여러 호출이 필요하다는 유일한 언급 / 힌트는 successCallback 매개 변수 에 대한 설명입니다 .

남아있는 파일이 없거나이 FileSystemDirectoryReader에서 이미 readEntries ()를 호출 한 경우 배열이 비어 있습니다.

틀림없이 API는 더 직관적 일 수 있지만 문서에서 알 수 있듯이 표준 트랙이 아닌 비표준 / 실험적 기능이며 모든 브라우저에서 작동 할 것으로 기대할 수 없습니다.

관련 :

  • johnozbay 는 Chrome에서 readEntries디렉토리에 대해 최대 100 개의 항목을 반환 할 것이라고 설명 합니다 (Chrome 64로 확인 됨).
  • XanreadEntries 은이 답변 에서 코드가 없지만 올바른 사용법을 설명합니다 .
  • Pablo Barría Urenda의 답변readEntriesBFS없이 비동기 방식으로 올바르게 호출 됩니다. 그는 또한 Firefox가 디렉토리의 모든 항목을 반환하지만 (Chrome과 달리) 사양이 주어지면이를 신뢰할 수 없다고 지적합니다.

에서 이 메시지 HTML 5 메일 링리스트 이안 힉슨은 말합니다 :

이제 HTML5는 한 번에 많은 파일을 업로드해야합니다. 브라우저를 통해 사용자는 여러 디렉토리를 포함하여 한 번에 여러 파일을 선택할 수 있습니다. 사양 범위를 약간 벗어났습니다.

(또한 원래 기능 제안을 참조하십시오 .) 따라서 그가 드래그 앤 드롭을 사용하여 폴더를 업로드하는 것도 범위를 벗어난다고 가정하는 것이 안전합니다. 분명히 개별 파일을 제공하는 것은 브라우저에 달려 있습니다.

Lars Gunther가 설명한대로 폴더를 업로드하는 데는 다른 어려움도 있습니다 .

이 […] 제안 에는 두 가지 확인 사항있어야 합니다 (가능한 경우).

  1. 압축되지 않은 수백 개의 원시 이미지의 전체 디렉토리를 누군가가 업로드하지 못하도록하는 최대 크기 ...

  2. accept 속성이 생략 된 경우에도 필터링. Mac OS 메타 데이터 및 Windows 썸네일 등은 생략해야합니다. 모든 숨김 파일 및 디렉터리는 기본적으로 제외되어야합니다.


이제 끌어서 놓기 및 입력으로 디렉토리를 업로드 할 수 있습니다.

<input type='file' webkitdirectory >

드래그 앤 드롭 (웹킷 브라우저 용).

드래그 앤 드롭 폴더 처리.

<div id="dropzone"></div>
<script>
var dropzone = document.getElementById('dropzone');
dropzone.ondrop = function(e) {
  var length = e.dataTransfer.items.length;
  for (var i = 0; i < length; i++) {
    var entry = e.dataTransfer.items[i].webkitGetAsEntry();
    if (entry.isFile) {
      ... // do whatever you want
    } else if (entry.isDirectory) {
      ... // do whatever you want
    }
  }
};
</script>

자원:

http://updates.html5rocks.com/2012/07/Drag-and-drop-a-folder-onto-Chrome-now-available


Firefox는 이제 2016 년 11 월 15 일부터 v50.0에서 폴더 업로드를 지원합니다. https://developer.mozilla.org/en-US/Firefox/Releases/50#Files_and_directories

Firefox로 폴더를 끌어다 놓거나 업로드 할 로컬 폴더를 찾아서 선택할 수 있습니다. 또한 하위 폴더에 중첩 된 폴더도 지원합니다.

즉, 이제 Chrome, Firefox, Edge 또는 Opera를 사용하여 폴더를 업로드 할 수 있습니다. 현재 Safari 또는 Internet Explorer를 사용할 수 없습니다.


이 함수는 다음과 같이 드롭 된 모든 파일의 배열에 대한 약속을 제공합니다 <input type="file"/>.files.

function getFilesWebkitDataTransferItems(dataTransferItems) {
  function traverseFileTreePromise(item, path='') {
    return new Promise( resolve => {
      if (item.isFile) {
        item.file(file => {
          file.filepath = path + file.name //save full path
          files.push(file)
          resolve(file)
        })
      } else if (item.isDirectory) {
        let dirReader = item.createReader()
        dirReader.readEntries(entries => {
          let entriesPromises = []
          for (let entr of entries)
            entriesPromises.push(traverseFileTreePromise(entr, path + item.name + "/"))
          resolve(Promise.all(entriesPromises))
        })
      }
    })
  }

  let files = []
  return new Promise((resolve, reject) => {
    let entriesPromises = []
    for (let it of dataTransferItems)
      entriesPromises.push(traverseFileTreePromise(it.webkitGetAsEntry()))
    Promise.all(entriesPromises)
      .then(entries => {
        //console.log(entries)
        resolve(files)
      })
  })
}

용법:

dropArea.addEventListener("drop", function(event) {
  event.preventDefault();

  var items = event.dataTransfer.items;
  getFilesFromWebkitDataTransferItems(items)
    .then(files => {
      ...
    })
}, false);

npm 패키지

https://www.npmjs.com/package/datatransfer-files-promise

사용 예 : https://github.com/grabantot/datatransfer-files-promise/blob/master/index.html


HTML5 사양에는 업로드 할 폴더를 선택할 때 브라우저가 포함 된 모든 파일을 재귀 적으로 업로드해야한다고 명시되어 있지 않습니다.

사실 Chrome / Chromium에서는 폴더를 업로드 할 수 있지만 그렇게하면 디렉토리를 나타내는 의미없는 4KB 파일 만 업로드됩니다. Alfresco 와 같은 일부 서버 측 응용 프로그램은 이를 감지 할 수 있으며 사용자에게 폴더를 업로드 할 수 없음을 경고합니다.

다음은 폴더이거나 크기가 0 바이트이므로 업로드 할 수 없습니다. undefined


다음은 파일 및 디렉토리 항목 API 를 사용하는 방법의 전체 예입니다 .

var dropzone = document.getElementById("dropzone");
var listing = document.getElementById("listing");

function scanAndLogFiles(item, container) {
  var elem = document.createElement("li");
  elem.innerHTML = item.name;
  container.appendChild(elem);

  if (item.isDirectory) {
    var directoryReader = item.createReader();
    var directoryContainer = document.createElement("ul");
    container.appendChild(directoryContainer);

    directoryReader.readEntries(function(entries) {
      entries.forEach(function(entry) {
        scanAndLogFiles(entry, directoryContainer);
      });
    });
  }
}

dropzone.addEventListener(
  "dragover",
  function(event) {
    event.preventDefault();
  },
  false
);

dropzone.addEventListener(
  "drop",
  function(event) {
    var items = event.dataTransfer.items;

    event.preventDefault();
    listing.innerHTML = "";

    for (var i = 0; i < items.length; i++) {
      var item = items[i].webkitGetAsEntry();

      if (item) {
        scanAndLogFiles(item, listing);
      }
    }
  },
  false
);
body {
  font: 14px "Arial", sans-serif;
}

#dropzone {
  text-align: center;
  width: 300px;
  height: 100px;
  margin: 10px;
  padding: 10px;
  border: 4px dashed red;
  border-radius: 10px;
}

#boxtitle {
  display: table-cell;
  vertical-align: middle;
  text-align: center;
  color: black;
  font: bold 2em "Arial", sans-serif;
  width: 300px;
  height: 100px;
}
<p>Drag files and/or directories to the box below!</p>

<div id="dropzone">
  <div id="boxtitle">
    Drop Files Here
  </div>
</div>

<h2>Directory tree:</h2>

<ul id="listing"></ul>

webkitGetAsEntry Chrome 13 이상, Firefox 50 이상 및 Edge에서 지원됩니다.

출처 : https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry


HTML5는 폴더 또는 폴더 트리의 드래그 드롭 업로드를 허용합니까?

이 기능은 Chrome에서만 지원합니다. 견인력이 없어 제거 될 가능성이 있습니다.

참고 : https://developer.mozilla.org/en/docs/Web/API/DirectoryReader#readEntries

참고 URL : https://stackoverflow.com/questions/3590058/does-html5-allow-drag-drop-upload-of-folders-or-a-folder-tree

반응형