HTML5 和 Javascript:使用文件 API 打开和读取本地文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10132018/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
HTML5 and Javascript : Opening and Reading a Local File with File API
提问by holocron
I am using Google Web Toolkit for a project and would like the user to select a text file to open in a text window inside the browser. Here's the almost working code:
我正在为一个项目使用 Google Web Toolkit,并希望用户选择一个文本文件以在浏览器内的文本窗口中打开。这是几乎可以工作的代码:
private DialogBox createUploadBox() {
final DialogBox uploadBox = new DialogBox();
VerticalPanel vpanel = new VerticalPanel();
String title = "Select a .gms file to open:";
final FileUpload upload = new FileUpload();
uploadBox.setText(title);
uploadBox.setWidget(vpanel);
HorizontalPanel buttons = new HorizontalPanel();
HorizontalPanel errorPane = new HorizontalPanel();
Button openButton = new Button( "Open", new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
String filename = upload.getFilename();
int len = filename.length();
if (len < 5) {
Window.alert("Please enter a valid filename.\n\tFormat: <filename>.gms");
} else if (!filename.substring(len-4).toLowerCase().equals(".gms")) {
Window.alert(filename.substring(len-4) + " is not valid.\n\tOnly files of type .gms are allowed.");
} else {
Window.alert(getFileText(filename));
}
}
private native String getFileText(String filename) /*-{
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
var reader = new FileReader();
var file = File(filename);
str = reader.readAsText(file);
return str;
} else {
alert('The File APIs are not fully supported in this browser.');
return;
}
}-*/;
});
Button cancelButton = new Button( "Cancel",
new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
uploadBox.hide();
}
});
buttons.add(openButton);
buttons.add(cancelButton);
vpanel.add(upload);
vpanel.add(buttons);
vpanel.add(errorPane);
uploadBox.setAnimationEnabled(true);
uploadBox.setGlassEnabled(true);
uploadBox.center();
return uploadBox;
}
Whenever I try to actually use this function in my program, I get:
每当我尝试在我的程序中实际使用这个函数时,我得到:
(NS_ERROR_DOM_SECURITY_ERR): Security error
(NS_ERROR_DOM_SECURITY_ERR):安全错误
I'm certain it is being cased by:
我确定它是由以下原因引起的:
var file = new File(filename, null);
Disclaimer: I'm not a Javascript programmer by any stretch, please feel free to point out any obvious mistakes I'm making here.
免责声明:我无论如何都不是 Javascript 程序员,请随时指出我在这里犯的任何明显错误。
回答by Colin Alworth
Instead of using window
, you should almost always use $wnd
. See https://developers.google.com/web-toolkit/doc/latest/DevGuideCodingBasicsJSNI#writingfor more details about JSNI.
而不是使用window
,您应该几乎总是使用$wnd
. 有关 JSNI 的更多详细信息,请参阅https://developers.google.com/web-toolkit/doc/latest/DevGuideCodingBasicsJSNI#writing。
It could also be worthwhile to add a debugger
statement while using something like Firebug, or Chrome's Inspector. This statement will stop the JS code in the debugger as if you had put a breakpoint there, are allow you to debug in Javascript, stepping one line at a time to see exactly what went wrong.
debugger
在使用诸如 Firebug 或 Chrome 的 Inspector 之类的东西时添加一条语句也是值得的。该语句将停止调试器中的 JS 代码,就像您在那里放置了一个断点一样,允许您在 Javascript 中进行调试,一次步进一行以查看到底出了什么问题。
And finally, are you sure the file you are reading is permitted by the browser? From http://dev.w3.org/2006/webapi/FileAPI/#dfn-SecurityError, that error could be occurring because the browser has not been permitted access to the file. Instead of passing in the String, you might pass in the <input type='file' />
the user is interacting with, and get the file they selected from there.
最后,您确定浏览器允许您阅读的文件吗?从http://dev.w3.org/2006/webapi/FileAPI/#dfn-SecurityError,可能会发生该错误,因为浏览器未被允许访问该文件。您可以传入<input type='file' />
用户与之交互的字符串,而不是传入字符串,并从那里获取他们选择的文件。
Update (sorry for the delay, apparently the earlier update I made got thrown away, took me a bit to rewrite it):
更新(抱歉耽搁了,显然我之前的更新被扔掉了,花了我一点时间来重写它):
Couple of bad assumptions being made in the original code. Most of my reading is from http://www.html5rocks.com/en/tutorials/file/dndfiles/, plus a bit of experimenting.
在原始代码中做出了一些错误的假设。我的大部分阅读来自http://www.html5rocks.com/en/tutorials/file/dndfiles/,加上一些实验。
- First, that you can get a real path from the
<input type='file' />
field, coupled with - that you can read arbitrary files from the user file system just by path, and finally
- that the
FileReader
API is synchronous.
- 首先,您可以从
<input type='file' />
现场获得一条真实的路径,再加上 - 您可以仅通过路径从用户文件系统中读取任意文件,最后
- 该
FileReader
API是同步的。
For security reasons, most browsers do not give real paths when you read the filename - check the string you get from upload.getFilename()
in several browsers to see what it gives - not enough to load the file. Second issue is also a security thing - very little good can come of allowing reading from the filesystem just using a string to specify the file to read.
出于安全原因,大多数浏览器在您读取文件名时不会提供真实路径 - 检查您upload.getFilename()
在多个浏览器中获取的字符串以查看它提供的内容 - 不足以加载文件。第二个问题也是一个安全问题 - 允许仅使用字符串指定要读取的文件从文件系统读取几乎没有好处。
For these first two reasons, you instead need to ask the input
for the files it is working on. Browsers that support the FileReader API allow access to this by reading the files
property of the input element. Two easy ways to get this - working with the NativeElement.getEventTarget() in jsni, or just working with FileUpload.getElement(). Keep in mind that this files
property holds multiple items by default, so in a case like yours, just read the zeroth element.
由于前两个原因,您需要询问正在input
处理的文件。支持 FileReader API 的浏览器允许通过读取files
input 元素的属性来访问它。两种简单的方法来获得它 - 使用 jsni 中的 NativeElement.getEventTarget(),或者只使用 FileUpload.getElement()。请记住,files
默认情况下此属性包含多个项目,因此在像您这样的情况下,只需读取第零个元素。
private native void loadContents(NativeEvent evt) /*-{
if ($wnd.File && $wnd.FileReader && $wnd.FileList && $wnd.Blob) {
// Great success! All the File APIs are supported.
var reader = new FileReader();
reader.readAsText(evt.target.files[0]);
//...
or
或者
private native void loadContents(Element elt) /*-{
if ($wnd.File && $wnd.FileReader && $wnd.FileList && $wnd.Blob) {
// Great success! All the File APIs are supported.
var reader = new FileReader();
reader.readAsText(elt.files[0]);
//...
For the final piece, the FileReader
api is asynchronous - you don't get the full contents of the file right away, but need to wait until the onloadend
callback is invoked (again, from http://www.html5rocks.com/en/tutorials/file/dndfiles/). These files can be big enough that you wouldn't want the app to block while it reads, so apparently the spec assumes this as the default.
对于最后一部分,FileReader
api 是异步的 - 您不会立即获得文件的全部内容,而是需要等到onloadend
回调被调用(同样,来自http://www.html5rocks.com/en/tutorials /文件/dndfiles/)。这些文件可能足够大,以至于您不希望应用程序在读取时被阻止,因此显然规范假定这是默认值。
This is why I ended up making a new void loadContents
methods, instead of keeping the code in your onClick
method - this method is invoked when the field's ChangeEvent
goes off, to start reading in the file, though this could be written some other way.
这就是为什么我最终创建了一个新void loadContents
方法,而不是将代码保留在您的onClick
方法中 - 当字段ChangeEvent
关闭时调用此方法以开始读取文件,尽管这可以以其他方式编写。
// fields to hold current state
private String fileName;
private String contents;
public void setContents(String contents) {
this.contents = contents;
}
// helper method to read contents asynchronously
private native void loadContents(NativeEvent evt) /*-{;
if ($wnd.File && $wnd.FileReader && $wnd.FileList && $wnd.Blob) {
var that = this;
// Great success! All the File APIs are supported.
var reader = new FileReader();
reader.readAsText(evt.target.files[0]);
reader.onloadend = function(event) {
[email protected]::setContents(Ljava/lang/String;)(event.target.result);
};
} else {
$wnd.alert('The File APIs are not fully supported in this browser.');
}
}-*/;
// original createUploadBox
private DialogBox createUploadBox() {
final DialogBox uploadBox = new DialogBox();
VerticalPanel vpanel = new VerticalPanel();
String title = "Select a .gms file to open:";
final FileUpload upload = new FileUpload();
upload.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
loadContents(event.getNativeEvent());
fileName = upload.getFilename();
}
});
// continue setup
The 'Ok' button then reads from the fields. It would probably be wise to check that contents is non null in the ClickHandler
, and perhaps even null it out when the FileUpload
's ChangeEvent
goes off.
然后“确定”按钮从字段中读取。检查 中的内容是否为非空值可能是明智的ClickHandler
,甚至可能在FileUpload
sChangeEvent
熄灭时将其归零。
回答by Kristoffer Sall-Storgaard
As far as I can see you can only use new File(name)
when writing extensions: https://developer.mozilla.org/en/Extensions/Using_the_DOM_File_API_in_chrome_code
据我所知,您只能new File(name)
在编写扩展时使用:https: //developer.mozilla.org/en/Extensions/Using_the_DOM_File_API_in_chrome_code