Reputation: 17904
I've been trying this for hours, so maybe a fresh set of eyes will help.
I'm trying to upload a file to the server using GWT (using UiBinder) on Google App Engine. Following all the examples I can find, things look like they should work, but the server's 'post' method is showing 0 items uploaded.
Here's the client code:
FormUploader.ui.xml:
<g:FormPanel action="/formImageUploader" ui:field="formPanel">
<g:VerticalPanel>
<g:FileUpload ui:field="uploader"></g:FileUpload>
<g:Button ui:field="submit">Submit</g:Button>
</g:VerticalPanel>
</g:FormPanel>
FormUploader.java:
@UiField
FormPanel formPanel;
@UiField
FileUpload uploader;
@UiField
Button submit;
public FormUploader() {
initWidget(uiBinder.createAndBindUi(this));
formPanel.setEncoding(FormPanel.ENCODING_MULTIPART);
formPanel.setMethod(FormPanel.METHOD_POST);
addSubmitHandlers();
}
private void addSubmitHandlers() {
formPanel.addSubmitCompleteHandler(new SubmitCompleteHandler() {
@Override
public void onSubmitComplete(SubmitCompleteEvent event) {
Core.log("Inside submitComplete");
Window.alert(event.getResults());
}
});
}
web.xml:
<servlet>
<servlet-name>formImageUploader</servlet-name>
<servlet-class>com.company.server.FormImageUploader</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>formImageUploader</servlet-name>
<url-pattern>/formImageUploader</url-pattern>
</servlet-mapping>
FormImageUploader.java:
public class FormImageUploader extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
log.info("Request.getContentLength(): " + request.getContentLength());
log.info("Content type: " + request.getContentType());
log.info("Parm names: ");
for (Object paramName : request.getParameterMap().keySet()) {
log.info("__Param name: " + paramName.toString());
}
log.info("Attribtue names: ");
Enumeration enu = request.getAttributeNames();
while (enu.hasMoreElements()) {
log.info("__Attribute name: " + enu.nextElement().toString());
}
log.info("Header names: ");
enu = request.getHeaderNames();
while (enu.hasMoreElements()) {
log.info("__Header name: " + enu.nextElement().toString());
}
resp.getWriter().println("Post Code has finished executing");
}
All this executes just fine, the client shows an alert box containing Post Code has finished executing, but I can't figure out how to get the content of the file I want to upload.
Here's the server side log:
Request.getContentLength(): 63
Content type: multipart/form-data; boundary=---------------------------194272772210834546661
Parm names:
Attribtue names:
__Attribute name: com.google.apphosting.runtime.jetty.APP_VERSION_REQUEST_ATTR
Can anyone help me see why I'm not seeing any files on the server after clicking "submit" on the client?
Thanks in advance
Edit:
Still can't figure out why I can't get the form data on the server. I'm following this example, and just trying to write the response out to the client, but the server is definitely not seeing any content from the client.
Server code:
try {
log.info("Inside try block");
ServletFileUpload upload = new ServletFileUpload();
log.info("created the servlet file upload");
res.setContentType("text/plain");
log.info("set the content type to text/plain");
FileItemIterator iterator = upload.getItemIterator(req);
log.info("Got the iterator for items");
while (iterator.hasNext()) {
log.info("__inside iterator.hasNext");
FileItemStream item = iterator.next();
log.info("__assigned the file item stream");
InputStream stream = item.openStream();
log.info("__opened said stream");
if (item.isFormField()) {
log.warning("Got a form field: " + item.getFieldName());
} else {
log.warning("Got an uploaded file: " + item.getFieldName() +
", name = " + item.getName());
int len;
byte[] buffer = new byte[8192];
while ((len = stream.read(buffer, 0, buffer.length)) != -1) {
res.getOutputStream().write(buffer, 0, len);
}
log.info("Done writing the input to the output stream");
}
}
} catch (Exception ex) {
log.severe("Exception; " + ex);
throw new ServletException(ex);
}
log.info("Done parseInput3");
Logs:
Inside parseInput3
Inside try block
created the servlet file upload
set the content type to text/plain
Got the iterator for items
Done parseInput3
Again, it's definitely not able to iterate over the file items on the server... any idea why?
Upvotes: 2
Views: 2261
Reputation: 17904
Turns out, GAE has a completely different way of uploading files (Blobs): http://code.google.com/appengine/docs/java/blobstore/overview.html#Uploading_a_Blob
You need to supply the form's on the client with a Blobstore Upload URL (which times out) from the server, retrieved using:
blobstoreService.createUploadUrl('urlToRedirectToAfterUploadComplete')
After setting that upload URL on the form's action, the form will be submitted and the blobstore service will redirect to the supplied URL, which can then access the BlobKey
of the object stored by Google.
So with this, I added the following ChangeHandler
to my GWT FileUpload
:
private void addChangeListener() {
Core.log("Selected file: " + uploader.getFilename());
uploader.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
MyApp.SERVICES.getUploadUrl(new SuccessAsyncCallback<String>() {
@Override
public void onSuccess(String uploadUrl) {
form.setAction(uploadUrl);
form.submit();
Core.log("Submitted form with upload url: " + uploadUrl);
}
});
}
});
}
Then in the servlet I have the GAE redirect to after uploads:
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws Exception {
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
Map<String, List<BlobKey>> blobs = blobstoreService.getUploads(req);
// where 'uploader' is the name of the FileUploader (<input type='file'>) control on the client
List<BlobKey> blobKeys = blobs.get("uploader");
resp.getOutputStream().print(blobKeys.get(0).getKeyString());
}
Note!
GAE will throw a OutOfMemory exception when trying to get the uploaded blob's value if the FileUpload
control doesn't have a name="whateveryouwantthenametobe"
attribute defined, even if you tell it (using UiBinder) that the id is ui:field="nameofControl"
Upvotes: 2
Reputation: 7054
You cannot write to the local file system - you should take a look at the files api.
Upvotes: 0