HTML5和Javascript:使用File API打开和读取本地文件

时间:2012-04-12 21:19:34

标签: javascript html5 jsni fileapi

我正在使用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;
 }

每当我尝试在我的程序中实际使用此函数时,我得到:

(NS_ERROR_DOM_SECURITY_ERR):安全错误

我确定它是由以下人员提供的:

var file = new File(filename, null);

免责声明:我不是一个Javascript程序员,请随时指出我在这里犯的任何明显错误。

2 个答案:

答案 0 :(得分:3)

您应该几乎总是使用window,而不是使用$wnd。有关JSNI的更多详细信息,请参阅https://developers.google.com/web-toolkit/doc/latest/DevGuideCodingBasicsJSNI#writing

在使用Firebug或Chrome的Inspector之类的内容时添加debugger语句也是值得的。这个语句将停止调试器中的JS代码,就好像你在那里放了一个断点一样,允许你在Javascript中调试,一次踩一行来确切看错了。

最后,您确定浏览器允许您正在阅读的文件吗?从http://dev.w3.org/2006/webapi/FileAPI/#dfn-SecurityError开始,可能会发生该错误,因为浏览器未被允许访问该文件。您可以传入用户正在与之交互的<input type='file' />,而不是传递String,并从中获取他们选择的文件。


更新(抱歉延迟,显然我所做的早期更新被扔掉了,我带了一点重写它):

在原始代码中做出了一些错误的假设。我的大部分阅读都来自http://www.html5rocks.com/en/tutorials/file/dndfiles/,还有一些实验。

  • 首先,您可以从<input type='file' />字段获得真正的路径,再加上
  • 您可以通过路径从用户文件系统中读取任意文件,最后
  • FileReader API是同步的。

出于安全原因,大多数浏览器在您读取文件名时都没有提供真实的路径 - 检查在几个浏览器中从upload.getFilename()获得的字符串,看看它给出了什么 - 不足以加载文件。第二个问题也是一个安全问题 - 只需使用字符串来指定要读取的文件,就可以从文件系统中读取数据。

出于前两个原因,您需要向input询问其正在处理的文件。支持FileReader API的浏览器允许通过读取input元素的files属性来访问它。有两种简单的方法 - 在jsni中使用NativeElement.getEventTarget(),或者只使用FileUpload.getElement()。请记住,默认情况下,此files属性包含多个项目,因此在像您这样的情况下,只需读取第0个元素。

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]);
//...

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]);
//...

对于最后一篇文章,FileReader api是异步的 - 你没有立即得到文件的全部内容,但需要等到调用onloadend回调(再次,来自http://www.html5rocks.com/en/tutorials/file/dndfiles/)。这些文件可能足够大,以至于您不希望应用程序在读取时阻止,因此显然规范将此视为默认值。

这就是为什么我最终制作了一个新的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) {
            that.@com.sencha.gxt.examples.test.client.Test::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

然后“确定”按钮从字段中读取。检查ClickHandler中的内容是非空的可能是明智的,甚至可能在FileUpload的{​​{1}}关闭时将其清空。

答案 1 :(得分:0)

据我所知,您在编写扩展程序时只能使用new File(name)https://developer.mozilla.org/en/Extensions/Using_the_DOM_File_API_in_chrome_code