繁体   English   中英

使用GWT和AppEngine Blobstore进行多文件上传?

[英]Multiple file upload using GWT and AppEngine Blobstore?

我如何在GWT和AppEngine Blobstore中创建类似Gmail的现代多文件上传?

最常提出的解决方案是gwtupload ,这是由Manolo Carrasco编写的优秀GWT组件。 但是,最新版本0.6.6不适用于blobstore(至少我无法使其工作),并且它不支持多文件选择。 在最新的0.6.7快照中有一个用于多文件选择的补丁,但是虽然它允许选择多个文件(使用HTML5中的“multiple”属性),但仍然会在一个巨大的POST请求中发送它们(并显示进度整堆文件)。

关于SO还有其他问题(例如此处此处 ),但答案通常使用HTML5“multiple”属性并将它们作为一个大的POST请求发送。 它有效,但它不是我追求的。

尼克约翰逊写了一些关于此的精彩博文 他使用名为Plupload的常见且广为接受的JavaScript上传组件,并将文件上传到用Python编写的AppEngine-app。 Plupload支持不同的后端( 运行时 ),支持多个文件选择(HTML5,flash,Silverlight等),并处理上传进度和其他上传相关的客户端事件。

他的解决方案的问题是(1)它在Python中,(2)它在JavaScript中。 这是gwt-plupload进入图片的地方。 它是由SamuliJärvelä编写的用于Plupload的JSNI -wrapper,它允许在GWT环境中使用Plupload。 但是,该项目已经过时(自2010年以来没有提交),但我们可以将其用作灵感。

因此,下面是构建多文件上载组件的逐步说明。 这将全部在一个项目中,但它(特别是JSNI-wrapper)可以被提取到它自己的.jar文件或库中,以便在其他项目中重用。 源代码可在Bitbucket上找到

该应用程序可在AppEngine上获得(不可计费,因此不要指望它可用或正常工作), 网址http://gwt-gaemultiupload-example.appspot.com/

截图

示例应用截图示例应用截图示例应用截图

第1步 - Servlets

Blobstore以下列方式工作:

  1. 客户端要求blobstore提供可用于上载文件的URL。
  2. 客户端将文件POST到收到的URL。
  3. 收到整个POST后,blobstore会将客户端重定向到成功URL(在创建上载URL时指定)。

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

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"));
    }
}

然后,用于接收成功上载的servlet,它将blobkey打印到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.");
    }
}

我们还需要在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://<computername>:8888/ 这将导致以后出现问题,出于安全原因,Plupload将无法将文件POST到另一个域。 这只发生在本地开发服务器上,发布的应用程序只有一个URL。 通过编辑Eclipse中的运行配置,将-bindAddress <computername>添加到参数来修复它。 这将导致本地开发服务器在http://<computername>:8888/上托管Web应用程序。 您可能需要在GWT浏览器插件中允许<computername> ,以便在此更改后加载应用程序。

到目前为止,我们有我们需要的servlet。

第2步 - Plupload

下载Plupload(我使用的是最新版本,1.5.4),解压缩并将js文件夹复制到GWT应用程序中的war目录。 对于此示例,我们将不会使用jquery.plupload.queuejquery.ui.plupload因为我们将创建自己的GUI。 我们还需要从Google API下载的jQuery。

接下来,我们需要在我们的应用程序中包含JavaScripts,因此编辑index.html并将以下内容添加到<head>标记中。

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

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

第3步 - 把它放在一起

所以现在我们需要在GWT应用程序中实际使用Plupload。 我将以下内容添加到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" />

有一个用于浏览文件的按钮,一个开始上传的按钮和一个用于显示上传状态的CellTable。 Index.java ,我们按如下方式初始化Plupload:

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();

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

要显示准备上传的文件,我们只是需要将数据添加到tblFilesDataProvider从事件列表数据提供商UploadListener

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

为了显示进度,我们只需在通知进度发生变化时更新列表:

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

我们还为btnStart实现了一个click处理程序,它只是告诉Plupload开始上传。

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

现在可以选择文件,它们将被添加到待处理的上传列表中,我们可以开始上传。 剩下的唯一部分是实际使用我们之前实现的servlet。 目前,Plupload不知道POST上传到哪个URL,所以我们需要告诉它。 这是我对gwt-plupload源代码进行了更改的地方(除了小错误修复); 我向Plupload添加了一个名为fetchNewUploadUrl的函数。 它的作用是在我们之前定义的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);
}

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

@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()事件处理程序中设置该值

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

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

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

示例项目包含此示例代码。

最后的话

我不是专家GWT开发人员。 这是我在经历了数小时的挫折之后想出来的,而没有找到我追求的功能。 在我开始工作之后,我想我应该写一个完整的例子,因为我使用/遵循的每个组件/博客帖子等都留下了一些部分。 我并不认为这是最佳实践代码。 欢迎提出意见,改进和建议!

暂无
暂无

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

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