1

我正在为一个项目使用 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 程序员,请随时指出我在这里犯的任何明显错误。

4

2 回答 2

3

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

debugger在使用诸如 Firebug 或 Chrome 的 Inspector 之类的东西时添加一个语句也是值得的。该语句将停止调试器中的 JS 代码,就好像您在此处设置了断点一样,允许您在 Javascript 中进行调试,一次执行一行以查看到底出了什么问题。

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


更新(抱歉耽搁了,显然我之前做的更新被扔掉了,花了我一点时间重写它):

在原始代码中做出了几个错误的假设。我的大部分阅读内容来自http://www.html5rocks.com/en/tutorials/file/dndfiles/以及一些实验。

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

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

由于前两个原因,您需要询问input它正在处理的文件。支持 FileReader API 的浏览器允许通过读取files输入元素的属性来访问它。有两种简单的方法来实现这一点 - 在 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]);
//...

或者

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

对于最后一部分,FileReaderapi 是异步的 - 您不会立即获得文件的全部内容,而是需要等到onloadend回调被调用(再次,来自http://www.html5rocks.com/en/tutorials /文件/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

然后“确定”按钮从字段中读取。检查 ' 中的内容是否为非 null 可能是明智之举,ClickHandler甚至可能在FileUpload'ChangeEvent熄灭时将其设为 null。

于 2012-04-12T22:22:22.583 回答
0

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

于 2012-04-12T22:20:37.030 回答