本主题介绍如何使用文件 API 读取本地文件。
简介
Internet Explorer 10(及更高版本)能够以安全的方式从本地文件系统(客户端计算机)中读取文件,而无需扩展或插件。本主题涵盖以下与核心文件有关的任务:
- 使用
input元素以及拖放 (DnD) 框选择本地文件。 - 读取所选文件、显示其内容以及显示文件元数据,如大小、文件类型、创建日期等。
在图面上,访问本地文件系统似乎存在很大的安全漏洞。但是,其实包含了很多安全措施;尤其是只能访问文件(如果用户提供这样做的权限)。
注意 以下代码示例需要使用支持文件 API 的浏览器,如 Internet Explorer 10 或更高版本。
选择文件
用户有两种常见的方法来选择本地文件。最简单的方法是使用 input 元素。稍微复杂点的另一种方法是创建 DnD 框。
使用 input 元素选择文件
通过在 input 元素上使用 type="file",选择文件变得相对简单:
<input type="file" id="fileSelector" multiple accept="image/*">
单击所得的“浏览”按钮时,multiple 特性允许用户在随后的文件选择对话框中选择多个文件。accept 特性仅允许用户选择图形文件,如 .jpg、.gif、.bmp 等。 以下简单示例演示如何使用 input 元素选择多个图形文件:
<!DOCTYPE html>
<html>
<head>
<title><input> File Selection</title>
<meta http-equiv="X-UA-Compatible" content="IE=10">
</head>
<body>
<h1>HTML5 <input> File Selection</h1>
<h3>Example 1</h3>
<input type="file" id="fileSelector" multiple accept="image/*" /> <!-- By design, if you select the exact same files two or more times, the 'change' event will not fire. -->
<ul id="fileContentList" style="list-style-type: none;"></ul> <!-- This will be populated with <li> elements via JavaScript. -->
<script type="text/javascript">
var message = [];
if (!document.getElementById('fileSelector').files) {
message = '<p>The ' +
'<a href="http://dev.w3.org/2006/webapi/FileAPI/" target="_blank">File API</a>s ' +
'are not fully supported by this browser.</p>' +
'<p>Upgrade your browser to the latest version.</p>';
document.querySelector('body').innerHTML = message;
}
else {
document.getElementById('fileSelector').addEventListener('change', handleFileSelection, false); // Add an onchange event listener for the <input id="fileSelector"> element.
}
function handleFileSelection(evt) {
var files = evt.target.files; // The files selected by the user (as a FileList object).
if (!files) {
msa.alert("<p>At least one selected file is invalid - do not select any folders.</p><p>Please reselect and try again.</p>");
return;
}
// The variable "files" is an array of file objects.
for (var i = 0, file; file = files[i]; i++) {
var img_element = document.createElement('img'); // We've only allowed the user to select graphics files, so get ready to display them.
img_element.src = window.URL.createObjectURL(file); // Assumes "file" is some sort of graphics file type.
img_element.width = 150; // Make all images the same width.
img_element.style.verticalAlign = "middle"; // Center the image in the middle of adjacent text.
img_element.style.margin = "4px 4px 4px 0";
img_element.onload = function() { window.URL.revokeObjectURL(this.src); } // The file URL is not needed once the file image has been fully loaded.
var span_element = document.createElement('span');
span_element.innerHTML = file.name;
var li_element = document.createElement('li');
li_element.appendChild(img_element);
li_element.appendChild(span_element);
document.getElementById('fileContentList').appendChild(li_element);
} // for
} // handleFileSelection
</script>
<script src="../utilities.js" type="text/javascript"></script> <!-- Provides the msa.alert() method. -->
</body>
</html>
在上面,<meta http-equiv="X-UA-Compatible" content="IE=10"> 指示 Windows Internet Explorer 采用 IE10 模式播放页面。有关详细信息,请参阅定义文档兼容性。
通常会将 script 块放置在 body 块的末尾,目的是提高性能,但更重要的是允许访问以前呈现的 DOM 元素,如 <input type="file" id="fileSelector" multiple accept="image/*" />。
示例 1 的算法非常简单:
-
如果未提供特定的文件 API 功能,则将所有之前的标记都替换为警告用户所需的文件 API 功能不可用的 HTML。否则,添加一个事件侦听器 (
handleFileSelection) 以侦听input元素上的任何更改。 -
当用户选择一个或多个文件时(通过
input元素),引发change事件,该事件调用handleFileSelection函数。传递给handleFileSelection的事件对象包含用户选择的文件列表,即evt.target.files。随后,对于文件列表中的每个文件对象,我们将创建图像缩略图列表并显示关联的文件名。
注意 由于 alert 不能在使用 JavaScript 的 Windows 应用商店应用中使用,因此我们转而使用 msa.alert。如果你不是为使用 JavaScript 的 Windows 应用商店应用进行开发,则可以将 msa.alert 替换为 JavaScript 的传统 alert,且功能不会有任何损失。
使用拖放框选择文件
之前用于选择文件的 input 元素技术相对来说比较简单,但可能看上去不太美观。更复杂的方法是创建文件 DnD 框,如下一示例中所示:
<!DOCTYPE html>
<html>
<head>
<title>Drag & Drop File Selection</title>
<meta http-equiv="X-UA-Compatible" content="IE=10">
<style>
#fileDropBox {
width: 20em;
line-height: 10em;
border: 1px dashed gray;
text-align: center;
color: gray;
border-radius: 7px;
</style>
</head>
<body>
<h1>HTML5 Drag and Drop File Selection</h1>
<h3>Example 2</h3>
<p>Using Windows Explorer (or similar), select one or more files (directories are not allowed), and then drag them to the below drop box:</p>
<div id="fileDropBox">Drop files here.</div>
<ul id="list"></ul>
<script>
var message = [];
if (!window.FileReader) {
message = '<p>The ' +
'<a href="http://dev.w3.org/2006/webapi/FileAPI/" target="_blank">File API</a>s ' +
'are not fully supported by this browser.</p>' +
'<p>Upgrade your browser to the latest version.</p>';
document.querySelector('body').innerHTML = message;
}
else {
// Set up the file drag and drop listeners:
document.getElementById('fileDropBox').addEventListener('dragover', handleDragOver, false);
document.getElementById('fileDropBox').addEventListener('drop', handleFileSelection, false);
}
function handleDragOver(evt) {
evt.stopPropagation(); // Do not allow the dragover event to bubble.
evt.preventDefault(); // Prevent default dragover event behavior.
} // handleDragOver()
function handleFileSelection(evt) {
evt.stopPropagation(); // Do not allow the drop event to bubble.
evt.preventDefault(); // Prevent default drop event behavior.
var files = evt.dataTransfer.files; // Grab the list of files dragged to the drop box.
if (!files) {
msa.alert("<p>At least one selected file is invalid - do not select any folders.</p><p>Please reselect and try again.</p>");
return;
}
// "files" is a FileList of file objects. List a few file object properties:
var output = [];
for (var i = 0, f; i < files.length; i++) {
try {
f = files[i]; // If anything goes awry, the error would occur here.
output.push('<li><strong>',
f.name, '</strong> (',
f.type || 'unknown file type',
') - ',
f.size, ' bytes, last modified: ',
f.lastModifiedDate,
'</li>');
document.getElementById('list').innerHTML = output.join('');
} // try
catch (fileError) {
msa.alert("<p>An unspecified file error occurred.</p><p>Selecting one or more folders will cause a file error.</p>");
console.log("The following error occurred at i = " + i + ": " + fileError); // Send the error object to the browser's debugger console window, if active.
return;
} // catch
} // for
} // handleFileSelection()
</script>
<script src="../utilities.js" type="text/javascript"></script> <!-- Provides the msa.alert() method. -->
</body>
</html>
文件 DnD 框只是一个 CSS 风格的 div 元素(该元素的 ID 为 fileDropBox),并有两个附加的事件侦听器。第一个事件侦听器 handleDragOver 用于将 dragover 事件置空。第二个 handleFileSelection 用于显示文件元数据信息(文件名、大小等)
调用 handleFileSelection 函数时,它将传递事件对象 (evt),该对象包含用户所选的文件列表:
var files = evt.dataTransfer.files;
随后,针对文件列表中的每个文件,我们将构建一个文件元数据信息的分类项目符号列表(推入 output 数组)。然后使用 <ul id=”list”> 元素通过以下方式显示这个项目符号列表:
document.getElementById('list').innerHTML = output.join('');
读取文件
现在我们可以选择文件了,那么下一步就是显示所选文本文件的内容。以下示例将完成此类操作。
<!DOCTYPE html>
<html>
<head>
<title>Drag & Drop File Selection</title>
<meta http-equiv="X-UA-Compatible" content="IE=10">
<style>
#fileDropBox {
width: 20em;
line-height: 10em;
border: 1px dashed gray;
text-align: center;
color: gray;
border-radius: 7px;
</style>
</head>
<body>
<h1>HTML5 Drag and Drop File Selection</h1>
<h3>Example 3</h3>
<p>Using Windows Explorer (or similar), select one or more text files (directories are not allowed), and then drag them to the below drop box:</p>
<div id="fileDropBox">Drop files here.</div>
<script>
var message = [];
if (!window.FileReader) {
message = '<p>The ' +
'<a href="http://dev.w3.org/2006/webapi/FileAPI/" target="_blank">File API</a>s ' +
'are not fully supported by this browser.</p>' +
'<p>Upgrade your browser to the latest version.</p>';
document.querySelector('body').innerHTML = message;
}
else {
// Set up the file drag and drop listeners:
document.getElementById('fileDropBox').addEventListener('dragover', handleDragOver, false);
document.getElementById('fileDropBox').addEventListener('drop', handleFileSelection, false);
}
function sanitizeHTML(htmlString) {
var tmp = document.createElement('div');
tmp.appendChild( document.createTextNode(htmlString) );
return tmp.innerHTML;
} // stripHtmlFromText
function handleDragOver(evt) {
evt.stopPropagation(); // Do not allow the dragover event to bubble.
evt.preventDefault(); // Prevent default dragover event behavior.
} // handleDragOver
function displayFileText(evt) {
var fileString = evt.target.result; // Obtain the file contents, which was read into memory.
//evt.target is a FileReader object, not a File object; so window.URL.createObject(evt.target) won't work here!
msa.alert("<pre>" + sanitizeHTML(fileString) + "</pre>", {width: 40, tile: true}); // sanitizeHTML() is used in case the user selects one or more HTML or HTML-like files and the <pre> tag preserves both spaces and line breaks.
} // displayFileText
function handleFileReadAbort(evt) {
msa.alert("File read aborted.");
} // handleFileReadAbort
function handleFileReadError(evt) {
var message;
switch (evt.target.error.name) {
case "NotFoundError":
msa.alert("The file could not be found at the time the read was processed.");
break;
case "SecurityError":
message = "<p>A file security error occured. This can be due to:</p>";
message += "<ul><li>Accessing certain files deemed unsafe for Web applications.</li>";
message += "<li>Performing too many read calls on file resources.</li>";
message += "<li>The file has changed on disk since the user selected it.</li></ul>";
msa.alert(message);
break;
case "NotReadableError":
msa.alert("The file cannot be read. This can occur if the file is open in another application.");
break;
case "EncodingError":
msa.alert("The length of the data URL for the file is too long.");
break;
default:
msa.alert("File error code " + evt.target.error.name);
} // switch
} // handleFileReadError
function startFileRead(fileObject) {
var reader = new FileReader();
// Set up asynchronous handlers for file-read-success, file-read-abort, and file-read-errors:
reader.onloadend = displayFileText; // "onloadend" fires when the file contents have been successfully loaded into memory.
reader.abort = handleFileReadAbort; // "abort" files on abort.
reader.onerror = handleFileReadError; // "onerror" fires if something goes awry.
if (fileObject) { // Safety first.
reader.readAsText(fileObject); // Asynchronously start a file read thread. Other supported read methods include readAsArrayBuffer() and readAsDataURL().
}
} // startFileRead
function handleFileSelection(evt) {
evt.stopPropagation(); // Do not allow the drop event to bubble.
evt.preventDefault(); // Prevent default drop event behavior.
var files = evt.dataTransfer.files; // Grab the list of files dragged to the drop box.
if (!files) {
msa.alert("<p>At least one selected file is invalid - do not select any folders.</p><p>Please reselect and try again.</p>");
return;
}
// "files" is a FileList of file objects. Try to display the contents of each file:
for (var i = 0, file; file = files[i]; i++) {
if (!file) {
msa.alert("Unable to access " + file.name);
continue; // Immediately move to the next file object.
}
if (file.size == 0) {
msa.alert("Skipping " + file.name.toUpperCase() + " because it is empty.");
continue;
}
if ( !file.type.match('text/.*') ) {
msa.alert("Skipping " + file.name.toUpperCase() + " because it is not a known text file type.");
continue;
}
startFileRead(file); // Asychronously fire off a file read request.
} // for
} // handleFileSelection
</script>
<script src="utilities.js" type="text/javascript"></script> <!-- Provides the msa.alert() method. -->
</body>
</html>
由于示例 3 基于示例 2,因此算法也类似:
-
如果未提供特定的文件 API 功能,则将所有之前的标记都替换为警告用户所需的文件 API 功能不可用的 HTML。否则,向 DnD 框中添加两个 DnD 事件侦听器:
<div id="fileDropBox">Drop files here.</div> -
当用户将文件从 Windows Explorer 窗口(或类似窗口)拖动到 DnD 框时,引发
drop事件并执行handleFileSelection函数。传递给handleFileSelection的事件 (evt) 包含用户选择的文件。 -
然后,
handleFileSelection循环它处理的每个文件(通过evt.dataTransfer.files),进行一些基本错误检查,并通过startFileRead(file)针对给定文件发出异步读取请求。当文件成功读入内存时,调用事件处理程序以对文件数据进行有用的操作,如下所述。 -
startFileRead(fileObject)函数为传递给它的每个文件创建一个新的 FileReader 对象。每个文件 FileReader 对象(例如代码中的reader)显示用于包含函数指针(即,事件处理程序)的属性。reader还显示很多文件读取方法,尤其是在reader.readAsText中的方法。当reasAsText方法成功(并异步)将文件的内容加载到内存中时,调用reader.onloadend中包含的函数指针。即,执行displayFileText事件处理程序。 -
传递给
displayFileText的事件对象包含读取的结果(由reader.reasAsText策划)。更确切说,evt.target.result包含针对关联文本文件请求读取的字符串格式的结果。然后,按照如下方式显示此字符串(即,文件的文本内容):msa.alert("<pre>" + sanitizeHTML(fileString) + "</pre>", {width: 40, tile: true});
如果用户选择一个或多个 HTML 或类似 HTML 的文件,则调用
santizeHTML以将“<" with "<", ">”替换为“>”等。使用<pre>标记保留文件内容的固有空格和换行。 如上所述,标准的alert方法与msa.alert的工作方式一样,但在使用 JavaScript 的 Windows 应用商店应用中禁止使用。msa.alert的第二个参数是向此方法传递可选参数的一个对象文本值。尤其是tile: true允许平铺多个警告框(即,轻微偏移)并且width: 40生成 40em 单位宽度的警告框。 - 返回到
startFileRead函数,如果发生文件读取错误或者用户取消读取操作,则会分别调用handleFileReadError和handleFileReadAbort事件处理程序。
现在,你应该能够使用 HTML5 和 JavaScript 读取本地文件(及其内容)了。也可尝试使用其他 FileReader 读取方法。尤其是,尝试使用 readAsArrayBuffer 方法编写自己的应用程序。
浙公网安备 33010602011771号