私はこれを行う例を見たことがありません。これはAPI仕様では許可されていませんか?
写真のフォルダツリー全体をアップロードするための簡単なドラッグアンドドロップソリューションを探しています。
私はこれを行う例を見たことがありません。これは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
、ディレクトリ内のすべての全体が返されるわけではないため、必ずしもChromiumの問題ではありません。:バグのリンクに基づいて、あなたの提供、私は完全な答えまで書いてきたstackoverflow.com/a/53058574/885922
残念ながら、特定のディレクトリのすべての(ファイルまたはディレクトリ)エントリが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もより直感的である可能性がありますが、ドキュメントに記載されているように、これは非標準/実験的な機能であり、標準トラックではなく、すべてのブラウザーで機能するとは限りません。
関連:
readEntries
、ディレクトリに対して最大100のエントリを返すとコメントしています(Chrome 64として確認済み)。readEntries
この回答で非常によく使用法を説明しています(コードはありませんが)。readEntries
、BFSを使用せずに非同期で正しく呼び出します。彼はまた、Firefoxは(Chromeとは異なり)ディレクトリ内のすべてのエントリを返すが、仕様を考えるとこれに依存することはできないと述べています。FileSystemFileEntry
をに変換する必要があります。フルパスも必要な場合は、から取得する必要があります(名前だけになります)。File
file(successCb, failureCb)
fileEntry.fullPath
file.webkitRelativePath
この関数は、次のように、ドロップされたすべてのファイルの配列を約束します<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);
https://www.npmjs.com/package/datatransfer-files-promise
使用例:https: //github.com/grabantot/datatransfer-files-promise/blob/master/index.html
function getFilesWebkitDataTransferItems(dataTransfer)
べきでfunction getFilesWebkitDataTransferItems(items)
あり、あるfor (entr of entries)
べきですfor (let entr of entries)
。
readEntries
は、空の配列を返すまで繰り返し呼び出す必要があることを規定しています。
で、このメッセージHTML 5メーリングリストへのIan Hickson氏は述べています:
HTML5は、一度に多くのファイルをアップロードする必要があります。ブラウザを使用すると、ユーザーは複数のディレクトリを含め、一度に複数のファイルを選択できます。それは仕様の範囲から少し外れています。
(元の機能の提案も参照してください。)したがって、ドラッグアンドドロップを使用してフォルダーをアップロードすることも範囲外であると彼が考えていると考えるのは安全です。どうやら、個々のファイルを提供するのはブラウザ次第です。
Lars Guntherが説明しているように、フォルダのアップロードには他にもいくつかの問題があります。
この[…]提案には、2つのチェックが必要です(実行可能かどうか)。
最大サイズ、誰かが数百の非圧縮生画像の完全なディレクトリをアップロードするのを防ぐために...
accept属性が省略されている場合でもフィルタリング。MacOSのメタデータやWindowsのサムネイルなどは省略してください。すべての隠しファイルとディレクトリは、デフォルトで除外されるはずです。
これで、ドラッグアンドドロップと入力の両方でディレクトリをアップロードできます。
<input type='file' webkitdirectory >
ドラッグアンドドロップ用(Webkitブラウザ用)。
ドラッグアンドドロップフォルダの処理。
<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やInternetExplorerはご利用いただけません。
ファイルおよびディレクトリエントリ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
readEntries
ものは、別の呼び出しreadEntries
がまだ実行されている場合は呼び出すことができません。DirectoryReader APIの設計は最適ではありません
更新:2012年以降、多くの変更がありました。代わりに上記の回答を参照してください。私は考古学のためにこの答えをここに残します。
HTML5仕様では、アップロードするフォルダーを選択するときに、ブラウザーは含まれているすべてのファイルを再帰的にアップロードする必要があるとは述べていません。
実際、Chrome / Chromiumではフォルダをアップロードできますが、アップロードすると、ディレクトリを表す意味のない4KBのファイルがアップロードされるだけです。Alfrescoなどの一部のサーバー側アプリケーションはこれを検出し、フォルダーをアップロードできないことをユーザーに警告できます。
最近、私の2つのプロジェクトでこれを実装する必要性に遭遇したので、これを支援するためのユーティリティ関数をたくさん作成しました。
1つは、すべてのフォルダー、ファイル、およびそれらの間の関係を表すデータ構造を作成します👇
{
folders: [
{
name: string,
folders: Array,
files: Array
},
/* ... */
],
files: Array
}
もう1つは、すべてのファイル(すべてのフォルダーとサブフォルダー内)の配列を返すだけです。
パッケージへのリンクは次のとおりです:https://www.npmjs.com/package/file-system-utils
input type=file
:stackoverflow.com/questions/9518335/...