使用GWT和AppEngine Blobstore多文件上传? [英] Multiple file upload using GWT and AppEngine Blobstore?

查看:287
本文介绍了使用GWT和AppEngine Blobstore多文件上传?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在GWT和AppEngine Blobstore中创建一个类似Gmail的多文件上传程序?

最常见的解决方案是 gwtupload 是由Manolo Carrasco编写的一个很好的GWT组件。但是,最新版本0.6.6不能用于blobstore(至少我不能得到它的工作),它不支持多个文件选择。在最新的0.6.7快照中有一个用于多文件选择的补丁,但是虽然它允许选择多个文件(使用HTML5中的multiple属性),但它仍然在一个巨大的POST请求中发送它们(并显示整个一堆文件)。



还有其他的问题在这个(例如这里 here ),但答案通常使用HTML5multiple属性,并将它们作为一个大的POST请求发送。它的工作原理,但它不是我所追求的。

解决方案

Nick Johnson写了一些好的博客文章。他使用通用的JavaScript上传组件 Plupload ,并将文件上传到AppEngine应用程序蟒蛇。 Plupload支持不同的后端( runtimes ),以支持多个文件选择(HTML5,Flash,Silverlight等),并处理上传进度和其他与上传相关的客户端事件。

他的解决方案的问题是(1)是Python,(2)是JavaScript。这是 gwt-plupload 进入图片的地方。这是一个由SamuliJärvelä编写的用于Plupload的 JSNI 打包程序,它启用在GWT环境中使用Plupload。但是,这个项目已经过时(2010年以来没有提交),但是我们可以用它来获得灵感。所以,一步一步地建立多个文件上传的说明组件如下。这将全部在一个项目中,但它(尤其是JSNI包装)可以被提取到它自己的.jar文件或库中,以便在其他项目中重用。源代码可在在Bitbucket 上获得。



应用程序在AppEngine上可用(不可计费,所以不要指望它是可用的或正在工作) http://gwt-gaemultiupload-example.appspot.com/

截图







第1步 - Servlet



Blobstore的工作方式如下:


  1. 客户端向blobstore询问可用于上传文件的URL。
  2. 客户端将文件发送到收到的URL。
  3. li>
  4. 收到整个POST时,blobstore会将客户端重定向到一个成功的URL(创建上传URL时指定)。


    为了支持这个,我们需要两个servlet的。一个用于生成文件上传的URL(注意每个文件上传将需要一个唯一的URL),一个用于接收完成的上传。两者都会很简单。下面是URL生成器servlet,它只是将纯文本的URL写入到HTTP响应中。



    pre $ public $ BlobstoreUrlGeneratorServlet extends HttpServlet {
    private static BlobstoreService blobstore = BlobstoreServiceFactory.getBlobstoreService();
    $ b @Override
    protected void doGet(HttpServletRequest req,HttpServletResponse resp)throws ServletException,IOException {
    resp.setHeader(Content-Type,text / plain);
    resp.getWriter()。write(blobstore.createUploadUrl(/ uploadfinished));


    然后,接收成功上传的servlet将打印blobkey到 System.out
    $ b

      public class BlobstoreUploadFinishedServlet extends HttpServlet {
    private static BlobstoreService blobstore = BlobstoreServiceFactory.getBlobstoreService();
    $ b $覆盖$ b $保护void doPost(HttpServletRequest req,HttpServletResponse resp)抛出ServletException,IOException {
    Map< String,List< BlobKey>> blobs = blobstore.getUploads(req);
    列表< BlobKey> blobKeyList = blobs.get(file);

    if(blobKeyList.size()== 0)
    return;

    BlobKey blobKey = blobKeyList.get(0);

    System.out.println(带有blobkey的文件+ blobKey.getKeyString()+被保存在blobstore中。);


    $ / code $ / pre
    $ b $我们还需要在 web.xml

     < servlet> 
    < servlet-name> urlGeneratorServlet< / servlet-name>
    < servlet-class> gaemultiupload.server.BlobstoreUrlGeneratorServlet< / servlet-class>
    < / servlet>
    < servlet-mapping>
    < servlet-name> urlGeneratorServlet< / servlet-name>
    < url-pattern> / generateblobstoreurl< / url-pattern>
    < / servlet-mapping>

    < servlet>
    < servlet-name> uploadFinishedServlet< / servlet-name>
    < servlet-class> gaemultiupload.server.BlobstoreUploadFinishedServlet< / servlet-class>
    < / servlet>
    < servlet-mapping>
    < servlet-name> uploadFinishedServlet< / servlet-name>
    < url-pattern> / uploadfinished< / url-pattern>
    < / servlet-mapping>

    如果我们现在运行应用程序并访问 http://127.0.0.1 :8888 / generateblobstoreurl ,我们会看到类似于

      http://< computername> ;: 8888 / _ah / upload / ahpnd3QtZ2FlbXVsdGl1cGxvYWQtZXhhbXBsZXIbCxIVX19CbG9iVXBsb2FkU2Vzc2lvbl9fGAEM 

    如果我们要将文件发布到该URL,保存在blobstore。但请注意,本地开发Web服务器的默认URL是 http://127.0.0.1:8888/ ,而blobstore生成的URL是 http ://<计算机名>:8888 / 。这将在稍后导致问题,至于安全原因Plupload将无法将文件POST到另一个域。这只发生在本地开发服务器上,发布的应用程序将只有一个URL。通过在Eclipse中编辑运行配置来修复它,将 -bindAddress< computername> 添加到参数中。这将导致本地开发服务器在 http://< computername>:8888 / 上托管Web应用程序。您可能需要在GWT浏览器插件中允许< computername> 来加载应用程序。



    步骤2 - Plupload



    下载Plupload (我使用的是最新版本,1.5.4),解压,并将 js 文件夹复制到 war 我们的GWT应用程序。对于这个例子,我们不会使用 jquery.plupload.queue jquery.ui.plupload 将创建我们自己的GUI。我们还需要jQuery,我从 Google API <然后编辑 index.html ,然后添加一个

    接下来,我们需要在我们的应用程序中包含JavaScript。以下为< head> 标签。

     <脚本type =text / javascriptlanguage =javascriptsrc =js / jquery.min.js>< / script> 
    < script type =text / javascriptlanguage =javascriptsrc =js / plupload.full.js>< / script>

    现在我们的应用程序中包含了Plupload。接下来,我们需要包装它,以便能够使用它与GWT。这是使用gwt-plupload的地方。我没有使用项目中的jar文件,而是复制了源文件,以便对其进行修改。包装的主要对象是由 PluploadBuilder 构造的 Plupload 类。还有一个接口 PluploadListener ,它可以被实现来接收客户端事件。

    第3步 - 把它所以现在我们需要在我们的GWT应用程序中实际使用Plupload。我将以下内容添加到 Index.ui.xml UIBinder:

      < g:按钮文字=浏览ui:field =btnBrowse/> 
    < br />
    < h:CellTable width =600pxui:field =tblFiles/>

    有一个浏览文件的按钮,一个开始上传的按钮和一个CellTable,我们将用它来显示上传状态。在 Index.java 中,我们初始化Plupload,如下所示:
    $ b $ pre
    tnBrowse。 getElement()SETID( BTN-浏览)。
    PluploadBuilder builder = new PluploadBuilder();
    builder.runtime(html5);
    builder.useQueryString(false);
    builder.multipart(true);
    builder.browseButton(btnBrowse.getElement()。getId());
    builder.listener(this);
    plupload = builder.create();
    plupload.init();

    运行时属性告诉Plupload后端使用(我只测试HTML5,但其他人也应该工作)。 Blobstore需要启用 multipart 。我们还需要为浏览按钮设置一个ID,然后告诉Plupload使用该ID。点击这个按钮将弹出Plupload的文件选择对话框。最后,我们将自己添加为侦听器(实现 PluploadListener )和 create() init ) Plupload。

    要显示准备上传的文件,我们只需要将数据添加到 tblFilesDataProvider code>在 UploadListener 中列出数据提供者。

      @Override 
    public void onFilesAdded(Plupload p,List< File> files){
    tblFilesDataProvider.getList()。addAll(files);

    $



    要显示进度,我们只要更新通知进度, :

      @Override 
    public void onFileUploadProgress(Plupload p,File file){
    tblFilesDataProvider.refresh );



    $ b我们也为 btnStart








    tnStart_Click(ClickEvent事件){
    plupload.start();





    $ b

    现在可以选择文件,它们将被添加到挂起的上传文件列表,我们可以开始上传。唯一剩下的就是实际使用我们之前实现的servlet。目前,Plupload不知道哪个URL上传到,所以我们需要告诉它。这是我对gwt-plupload源代码进行了修改的地方(除了小错误修正之外)。我添加了一个名为 fetchNewUploadUrl 的Plupload函数。它所做的是在我们之前定义的servlet上执行一个Ajax GET请求来获取一个上传URL。它是同步的(为什么稍后会清楚)。当请求返回时,它将该URL设置为Plupload的POST URL。

      private native void fetchNewUploadUrl(Plupload pl)/ * -  {
    $ wnd。$。ajax({
    url:'/ generateblobstoreurl',
    async:false,
    success:function(data){
    pl。 settings.url = data;
    },
    });
    } - * /;

    public void fetchNewUploadUrl(){
    fetchNewUploadUrl(this);



    $ b $ p
    $ b

    Plupload将在每个文件发布自己的POST请求。这意味着我们需要在每次上传开始之前给它一个新的URL。幸运的是,在 PluploadListener 中有一个我们可以实现的事件。这就是请求必须是同步的原因:否则,在我们收到上面的事件处理程序( pl.fetchNewUploadUrl())中的上传URL之前,

      @Override 
    public void onBeforeUpload(Plupload pl,File cast){
    pl .fetchNewUploadUrl();
    }

    就是这样!你现在有GWT HTML5多文件上传功能将文件放在AppEngine Blobstore中!

    传递参数



    如果你想到额外的参数(例如上传文件所属实体的ID),我添加了一个关于如何添加一个的例子。有一个方法在 Plupload 中调用 setExtraValue()我实现为:

      public native void setExtraValue(String value)/ *  -  {
    this.settings.multipart_params = {extravalue:value}
    } - * /;

    额外值可以作为 multipart_params 传递。这是一个映射,所以可以扩展功能以允许许多任意的键值对。可以在 onBeforeUpload()事件处理函数中设置该值
    $ b $ pre $ code> @覆盖
    public void onBeforeUpload(Plupload pl,File cast){
    pl.setExtraValue(System.currentTimeMillis()+是唯一的。
    pl.fetchNewUploadUrl();

    code


    在接收完成上传的servlet中检索到

     字符串值= req.getParameter(extravalue); 

    示例项目包含示例代码。



    结束语



    我不是专家级的GWT开发者。这是我经过几个小时的挫折之后找不到我以后的功能。我得到它的工作后,我想我应该写一个完整的的例子,因为每个组件/博客帖子/我用/跟随已经留下了一部分。我不暗示这是最好的实践代码。欢迎评论,改进和建议!


    How would I go about creating a modern, Gmail-like, multiple file upload in GWT and AppEngine Blobstore?

    The solution most commonly proposed is gwtupload, an excellent GWT component written by Manolo Carrasco. However, the latest version 0.6.6 does not work with blobstore (at least I can't get it to work), and it does not support multiple file select. There's a patch for multiple file select in the latest 0.6.7 snapshot, but although it allows selection of multiple files (using the "multiple" attribute in HTML5), it stills sends them in one huge POST request (and progress is shown for the whole bunch of files).

    There are also other questions on SO for this (for example here or here), but the answers usually uses the HTML5 "multiple" attribute and sends them as one big POST request. It works, but its not what I am after.

    解决方案

    Nick Johnson wrote some great blog posts about this. He uses the common and well-accepted JavaScript upload component called Plupload, and uploads files to an AppEngine-app written in Python. Plupload supports different backends (runtimes) for supporting multiple file selection (HTML5, flash, Silverlight, etc) and handles upload progress and other upload related client-side events.

    The problem with his solution is (1) it's in Python, and (2) it's in JavaScript. This is where gwt-plupload enters the picture. It is a JSNI-wrapper for Plupload written by Samuli Järvelä, which enables use of Plupload inside the GWT environment. However, the project is outdated (no commits since 2010), but we can use it for inspiration.

    So, step-by-step instructions for building the multiple file upload component follows. This will all be in one project, but it (especially the JSNI-wrapper) could be extracted to its own .jar-file or library to be reused in other projects. The source code is available on Bitbucket here.

    The application is available on AppEngine (non-billable, so don't count on it being available or working) at http://gwt-gaemultiupload-example.appspot.com/.

    Screenshots

    Step 1 - Servlets

    Blobstore works in the following way:

    1. Client asks blobstore for a URL it can use for uploading a file.
    2. Client POSTs the file to the received URL.
    3. When the whole POST is received, blobstore will redirect the client to a success URL (specified when creating the upload URL).

    To support this, we will need two servlets. One for generating URLs for file uploads (note that each file upload will need an unique URL), and one for receiving finished uploads. Both will be quite simple. Below is the URL generator servlet, which will just write the URL in plain text to the HTTP response.

    public class BlobstoreUrlGeneratorServlet extends HttpServlet {     
        private static BlobstoreService blobstore = BlobstoreServiceFactory.getBlobstoreService();
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.setHeader("Content-Type", "text/plain");
            resp.getWriter().write(blobstore.createUploadUrl("/uploadfinished"));
        }
    }
    

    And then, the servlet for receiving successful uploads, which will print the blobkey to System.out:

    public class BlobstoreUploadFinishedServlet extends HttpServlet {
        private static BlobstoreService blobstore = BlobstoreServiceFactory.getBlobstoreService();
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            Map<String, List<BlobKey>> blobs = blobstore.getUploads(req);
            List<BlobKey> blobKeyList = blobs.get("file");
    
            if (blobKeyList.size() == 0)
                return;
    
            BlobKey blobKey = blobKeyList.get(0);
    
            System.out.println("File with blobkey " + blobKey.getKeyString() + " was saved in blobstore.");
        }
    }
    

    We also need to register these in web.xml.

    <servlet>
        <servlet-name>urlGeneratorServlet</servlet-name>
        <servlet-class>gaemultiupload.server.BlobstoreUrlGeneratorServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>urlGeneratorServlet</servlet-name>
        <url-pattern>/generateblobstoreurl</url-pattern>
    </servlet-mapping>
    
    <servlet>
        <servlet-name>uploadFinishedServlet</servlet-name>
        <servlet-class>gaemultiupload.server.BlobstoreUploadFinishedServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>uploadFinishedServlet</servlet-name>
        <url-pattern>/uploadfinished</url-pattern>
    </servlet-mapping>
    

    If we run the app now and visit http://127.0.0.1:8888/generateblobstoreurl, we will see something like

    http://<computername>:8888/_ah/upload/ahpnd3QtZ2FlbXVsdGl1cGxvYWQtZXhhbXBsZXIbCxIVX19CbG9iVXBsb2FkU2Vzc2lvbl9fGAEM 
    

    If we were to post a file to that URL, it would be saved in blobstore. Note however that the default URL for the local development web server is http://127.0.0.1:8888/ while the URL generated by blobstore is http://<computername>:8888/. This will cause problems later on, as for security reasons Plupload won't be able to POST files to another domain. This only happens with the local development server, the published app will have only one URL. Fix it by editing the Run Configurations in Eclipse, add -bindAddress <computername> to the arguments. This will cause the local development server to host the web app on http://<computername>:8888/ instead. You might need to allow <computername> in the GWT browser plugin for it to load the app after this change.

    So far so good, we have the servlets we need.

    Step 2 - Plupload

    Download Plupload (I used the latest version, 1.5.4), unzip, and copy the js folder to the war directory in our GWT application. For this example, we won't be using jquery.plupload.queue or jquery.ui.plupload as we'll create our own GUI. We also need jQuery, which I downloaded from Google APIs.

    Next, we need to include the JavaScripts in our application, so edit index.html and add the following to the <head> tag.

    <script type="text/javascript" language="javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" language="javascript" src="js/plupload.full.js"></script>
    

    So now we have Plupload included in our application. Next, we need to wrap it to be able to use it with GWT. This is where gwt-plupload is used. I didn't use the jar file from the project, but instead copied the source files to be able to make modifications to them. The wrapper's main object is the Plupload class, which is constructed by PluploadBuilder. There's also the interface PluploadListener, which can be implemented to receive client-side events.

    Step 3 - Putting it together

    So now we need to actually use Plupload in our GWT application. I added the following to an Index.ui.xml UIBinder:

    <g:Button text="Browse" ui:field="btnBrowse" />
    <g:Button text="Start Upload" ui:field="btnStart" /><br />
    <br />
    <h:CellTable width="600px" ui:field="tblFiles" />
    

    There's a button for browsing files, a button to start uploading and a CellTable which we will use to display upload status. In Index.java, we initialize Plupload as follows:

    btnBrowse.getElement().setId("btn-browse");
    PluploadBuilder builder = new PluploadBuilder();
    builder.runtime("html5");
    builder.useQueryString(false);
    builder.multipart(true);
    builder.browseButton(btnBrowse.getElement().getId());
    builder.listener(this);
    plupload = builder.create();
    plupload.init();
    

    The runtime attribute tells Plupload which backends to use (I have only tested HTML5, but the others should work as well). Blobstore requires multipart to be enabled. We also need to set an ID for the browse button, and then tell Plupload to use that ID. Clicking this button will popup Plupload's file selection dialog. Last, we add ourselves as listener (implementing PluploadListener) and create() and init() Plupload.

    To display the files ready to upload, we just need to add data to the tblFilesDataProvider list data provider in the events from UploadListener.

    @Override
    public void onFilesAdded(Plupload p, List<File> files) {
        tblFilesDataProvider.getList().addAll(files);
    }
    

    To display progress, we simply update the list whenever we are notified progress have changed:

    @Override
    public void onFileUploadProgress(Plupload p, File file) {
        tblFilesDataProvider.refresh();
    }
    

    We also implement a click handler for btnStart, which justs tells Plupload to start uploading.

    @UiHandler("btnStart")
    void btnStart_Click(ClickEvent event) {
        plupload.start();
    }
    

    It is now possible to select files, they will be added to the pending uploads list and we can start the upload. The only piece left is to actually use the servlets we implemented earlier. Currently, Plupload does not know which URL to POST uploads to, so we need to tell it. This is where I have made a change to the gwt-plupload source code (apart from minor bug fixes); I added a function to Plupload called fetchNewUploadUrl. What it does is it performs an Ajax GET request at the servlet we definied earlier to fetch an upload URL. It does this synchronously (why will be clear later). When the requests returns, it sets this URL as the POST URL for Plupload.

    private native void fetchNewUploadUrl(Plupload pl) /*-{
        $wnd.$.ajax({
            url: '/generateblobstoreurl',
            async: false,
            success: function(data) {
              pl.settings.url = data;
            },
        });
    }-*/;
    
    public void fetchNewUploadUrl() {
        fetchNewUploadUrl(this);
    }
    

    Plupload will post each file in its own POST request. This means we need to give it a new URL before each upload starts. Luckily, there's an event for that in PluploadListener which we can implement. And here's the reason why the request has to be synchronous: otherwise the upload would have started before we received the upload URL in the event handler below (pl.fetchNewUploadUrl() would have returned immediately).

    @Override
    public void onBeforeUpload(Plupload pl, File cast) {
        pl.fetchNewUploadUrl();
    }
    

    And that's it! You now have GWT HTML5 multiple file upload functionality placing files in AppEngine Blobstore!

    Passing parameters

    If you want to additional parameters (such as an ID for the entity to which the uploaded files belong), I added an example on how to add one. There's a method on Plupload called setExtraValue() I implemented as:

    public native void setExtraValue(String value) /*-{
        this.settings.multipart_params = {extravalue: value}
    }-*/;
    

    Extra values can be passed as multipart_params. This is a map, so the functionality could be extended to allow many arbitrary key-value pairs. The value can be set in the onBeforeUpload() event handler

    @Override
    public void onBeforeUpload(Plupload pl, File cast) {
        pl.setExtraValue(System.currentTimeMillis() + " is unique.");
        pl.fetchNewUploadUrl();
    }
    

    and retrieved in the servlet receiving finished uploads as

    String value = req.getParameter("extravalue");
    

    The example project contains sample code for this.

    Final words

    I am no way an expert GWT developer. This is what I came up with after hours of frustration not finding the functionality I was after. After I got it working, I thought I should write up a complete example, as every component/blog post/etc I used/followed had left some part out. I do not imply this to be best practice code in any way. Comments, improvements and suggestions are welcome!

    这篇关于使用GWT和AppEngine Blobstore多文件上传?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆