[英]serving video from appengine blobstore behaves differently to static file
[英]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/ 。
Blobstore以下列方式工作:
为了支持这一点,我们需要两个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。
下载Plupload(我使用的是最新版本,1.5.4),解压缩并将js
文件夹复制到GWT应用程序中的war
目录。 对于此示例,我们将不会使用jquery.plupload.queue
或jquery.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
,可以实现接收客户端事件。
所以现在我们需要在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.