简体   繁体   English

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

[英]HTML5 and Javascript : Opening and Reading a Local File with File API

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. 我正在使用Google Web Toolkit进行项目,并希望用户选择要在浏览器内的文本窗口中打开的文本文件。 Here's the almost working code: 这是几乎可以工作的代码:

 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程序员,请随时指出我在这里犯的任何明显错误。

Instead of using window , you should almost always use $wnd . 你应该几乎总是使用$wnd而不是使用window See https://developers.google.com/web-toolkit/doc/latest/DevGuideCodingBasicsJSNI#writing for more details about JSNI. 有关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. 在使用Firebug或Chrome的Inspector之类的东西时添加debugger语句也是值得的。 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. 这个语句会停止调试器中的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. http://dev.w3.org/2006/webapi/FileAPI/#dfn-SecurityError ,可能发生该错误,因为浏览器未被允许访问该文件。 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. 您可以传入用户正在与之交互的<input type='file' /> ,而不是传入String,并从中获取他们选择的文件。


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 首先,你可以从<input type='file' />字段获得一个真实的路径,再加上
  • that you can read arbitrary files from the user file system just by path, and finally 您可以通过路径从用户文件系统中读取任意文件,最后
  • that the FileReader API is synchronous. 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. 出于安全原因,大多数浏览器在读取文件名时都没有提供真实的路径 - 在几个浏览器中检查从upload.getFilename()获得的字符串,看看它给出了什么 - 不足以加载文件。 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. 第二个问题也是一个安全问题 - 只需使用字符串来指定要读取的文件就可以从文件系统中读取,这样做很少。

For these first two reasons, you instead need to ask the input for the files it is working on. 出于前两个原因,您需要询问input它正在处理的文件。 Browsers that support the FileReader API allow access to this by reading the files property of the input element. 支持FileReader API的浏览器允许通过读取input元素的files属性来访问它。 Two easy ways to get this - working with the NativeElement.getEventTarget() in jsni, or just working with FileUpload.getElement(). 有两种简单的方法 - 在jsni中使用NativeElement.getEventTarget(),或者只使用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. 请记住,默认情况下,此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]);
//...

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/ ). 对于最后一篇文章, FileReader api是异步的 - 你不会立即得到文件的全部内容,但需要等到调用onloadend回调(再次,来自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. 这些文件可能足够大,您不希望应用程序在读取时阻止,因此显然规范将此视为默认值。

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) {
            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

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检查内容是非空的可能是明智的,甚至可能在FileUploadChangeEvent关闭时将其ClickHandler

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM