diff options
author | Matti Tahvonen <matti.tahvonen@itmill.com> | 2010-03-12 14:57:49 +0000 |
---|---|---|
committer | Matti Tahvonen <matti.tahvonen@itmill.com> | 2010-03-12 14:57:49 +0000 |
commit | f1d07c89844bbd3d46281eb95166f609bd4191e8 (patch) | |
tree | 744d90694ca2fe2deaddb2f50b77a008ccb9ad8d /src | |
parent | c1fee1ce305961af5576ea434685b392adc728b1 (diff) | |
download | vaadin-framework-f1d07c89844bbd3d46281eb95166f609bd4191e8.tar.gz vaadin-framework-f1d07c89844bbd3d46281eb95166f609bd4191e8.zip |
A proper (FF36 only) file drag support, enhanced test case. Should be forward compatible.
svn changeset:11835/svn branch:6.3
Diffstat (limited to 'src')
6 files changed, 221 insertions, 84 deletions
diff --git a/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java b/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java index 2beef216f2..606b382c26 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VDragAndDropWrapper.java @@ -13,6 +13,7 @@ import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DeferredCommand; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.xhr.client.XMLHttpRequest; import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.Paintable; @@ -193,44 +194,16 @@ public class VDragAndDropWrapper extends VCustomComponent implements transferable.setData("filecount", fileCount); for (int i = 0; i < fileCount; i++) { final int fileId = filecounter++; - final VHtml5File file = event.getFile(fileCount); - transferable.setData("fn" + fileId, file.getName()); - transferable.setData("ft" + fileId, file.getType()); - transferable.setData("fs" + fileId, file.getSize()); - DeferredCommand.addCommand(new Command() { - public void execute() { - /* - * File contents is sent deferred to allow quick - * reaction on GUI although file upload may last long. - * TODO make this use apache file upload instead of our - * variable post like in upload. Currently stalls the - * GUI during upload. Also need to use dataurl to - * support all possible bytes in file content - */ - file.readAsDataUrl(new Callback() { - public void handleFile(JavaScriptObject object) { - client.updateVariable(client - .getPid(VDragAndDropWrapper.this), - "file" + fileId, object.toString(), - true); - - } - }); - - } - }); + final VHtml5File file = event.getFile(i); + transferable.setData("fi" + i, "" + fileId); + transferable.setData("fn" + i, file.getName()); + transferable.setData("ft" + i, file.getType()); + transferable.setData("fs" + i, file.getSize()); + postFile(fileId, file); } } - // TODO remove this when above cleaner and more standard compliance - // system works - String fileAsString = event.getFileAsString(0); - if (fileAsString != null) { - ApplicationConnection.getConsole().log(fileAsString); - transferable.setData("fileContents", fileAsString); - } - VDragAndDropManager.get().endDrag(); vaadinDragEvent = null; event.preventDefault(); @@ -239,6 +212,97 @@ public class VDragAndDropWrapper extends VCustomComponent implements return false; } + static class ExtendedXHR extends XMLHttpRequest { + + protected ExtendedXHR() { + } + + public final native void sendBinary(JavaScriptObject data) + /*-{ + //this.overrideMimeType('text/plain; charset=x-user-defined-binary'); + this.sendAsBinary(data); + }-*/; + + } + + /** + * + * Currently supports only FF36 as no other browser supprots natively File + * api. + * + * @param fileId + * @param data + */ + private void postFile(final int fileId, final VHtml5File file) { + DeferredCommand.addCommand(new Command() { + public void execute() { + /* + * File contents is sent deferred to allow quick reaction on GUI + * although file upload may last long. TODO make this use apache + * file upload instead of our variable post like in upload. + * Currently stalls the GUI during upload. Also need to use + * dataurl to support all possible bytes in file content + */ + file.readAsBinary(new Callback() { + public void handleFile(final JavaScriptObject object) { + + DeferredCommand.addCommand(new Command() { + + public void execute() { + + ExtendedXHR extendedXHR = (ExtendedXHR) ExtendedXHR + .create(); + extendedXHR.open("POST", client.getAppUri()); + extendedXHR + .setRequestHeader( + "PaintableId", + client + .getPid(VDragAndDropWrapper.this)); + extendedXHR.setRequestHeader("FileId", "" + + fileId); + + // extendedXHR.setRequestHeader("Connection", + // "close"); + + multipartSend( + extendedXHR, + object, + "XHRFILE" + + client + .getPid(VDragAndDropWrapper.this) + + "." + fileId); + + } + }); + } + }); + + } + }); + + } + + private native void multipartSend(JavaScriptObject xhr, + JavaScriptObject data, String name) + /*-{ + + var boundaryString = "------------------------------------------VAADINXHRFILEUPLOAD"; + var boundary = "--" + boundaryString; + var CRLF = "\r\n"; + xhr.setRequestHeader("Content-type", "multipart/form-data; boundary=\"" + boundaryString + "\""); + var requestBody = boundary + + CRLF + + "Content-Disposition: form-data; name=\""+name+"\"; filename=\"file\"" + + CRLF + + "Content-Type: application/octet-stream" // hard coded, type sent separately + + CRLF + CRLF + data.target.result + CRLF + boundary + "--" + CRLF; + xhr.setRequestHeader("Content-Length", requestBody.length); + + + xhr.sendAsBinary(requestBody); + + }-*/; + public VDropHandler getDropHandler() { return dropHandler; } diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent.java index 9d5bafc2b7..47b1ba81ed 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent.java +++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5DragEvent.java @@ -59,7 +59,7 @@ public class VHtml5DragEvent extends NativeEvent { public final native VHtml5File getFile(int fileIndex) /*-{ - return this.dataTransfer.files[i]; + return this.dataTransfer.files[fileIndex]; }-*/; } diff --git a/src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5File.java b/src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5File.java index ead0ee6ceb..1a1db2910d 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5File.java +++ b/src/com/vaadin/terminal/gwt/client/ui/dd/VHtml5File.java @@ -14,17 +14,17 @@ public class VHtml5File extends JavaScriptObject { public native final String getName() /*-{ - return name; + return this.name; }-*/; public native final String getType() /*-{ - return type; + return this.type; }-*/; public native final int getSize() /*-{ - return size; + return this.size; }-*/; public native final void readAsBinary(final Callback callback) @@ -33,7 +33,9 @@ public class VHtml5File extends JavaScriptObject { r.onloadend = function(content) { callback.@com.vaadin.terminal.gwt.client.ui.dd.VHtml5File.Callback::handleFile(Lcom/google/gwt/core/client/JavaScriptObject;)(content); }; - r.readAsBinary(this); + r.readAsBinaryString(this); + var j = 0; + }-*/; public native final void readAsDataUrl(final Callback callback) diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java index d1b8ec7671..d3243fab73 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java @@ -1252,6 +1252,8 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements return RequestType.STATIC_FILE; } else if (isApplicationRequest(request)) { return RequestType.APPLICATION_RESOURCE; + } else if (request.getHeader("FileId") != null) { + return RequestType.FILE_UPLOAD; } return RequestType.OTHER; diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index 481e42c0e9..ada3869269 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -62,6 +62,7 @@ import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.server.ComponentSizeValidator.InvalidLayout; import com.vaadin.ui.AbstractField; import com.vaadin.ui.Component; +import com.vaadin.ui.DragAndDropWrapper; import com.vaadin.ui.Upload; import com.vaadin.ui.Window; import com.vaadin.ui.Upload.UploadException; @@ -359,6 +360,7 @@ public abstract class AbstractCommunicationManager implements */ protected void doHandleFileUpload(Request request, Response response) throws IOException, FileUploadException { + // Create a new file upload handler final FileUpload upload = createFileUpload(); @@ -386,22 +388,6 @@ public abstract class AbstractCommunicationManager implements if (item.isFormField()) { // ignored, upload requests contains only files } else { - int separatorPos = name.lastIndexOf("_"); - final String pid = name.substring(0, separatorPos); - final Upload uploadComponent = (Upload) idPaintableMap - .get(pid); - if (uploadComponent == null) { - throw new FileUploadException( - "Upload component not found"); - } - if (uploadComponent.isReadOnly()) { - throw new FileUploadException( - "Warning: ignored file upload because upload component is set as read-only"); - } - synchronized (application) { - // put upload component into receiving state - uploadComponent.startUpload(); - } final UploadStream upstream = new UploadStream() { public String getContentName() { @@ -422,22 +408,55 @@ public abstract class AbstractCommunicationManager implements }; - // tell UploadProgressListener which component is receiving - // file - pl.setUpload(uploadComponent); + if (name.startsWith("XHRFILE")) { + String[] split = item.getFieldName().substring(7) + .split("\\."); + DragAndDropWrapper ddw = (DragAndDropWrapper) idPaintableMap + .get(split[0]); + + ddw.receiveFile(upstream, split[1]); - try { - uploadComponent.receiveUpload(upstream); - } catch (UploadException e) { - // error happened while receiving file. Handle the - // error in the same manner as it would have happened in - // variable change. + String debugId = ddw.getDebugId(); + + } else { + + int separatorPos = name.lastIndexOf("_"); + final String pid = name.substring(0, separatorPos); + final Upload uploadComponent = (Upload) idPaintableMap + .get(pid); + if (uploadComponent == null) { + throw new FileUploadException( + "Upload component not found"); + } + if (uploadComponent.isReadOnly()) { + throw new FileUploadException( + "Warning: ignored file upload because upload component is set as read-only"); + } synchronized (application) { - handleChangeVariablesError(application, - uploadComponent, e, - new HashMap<String, Object>()); + // put upload component into receiving state + uploadComponent.startUpload(); + } + + // tell UploadProgressListener which component is + // receiving + // file + pl.setUpload(uploadComponent); + + try { + uploadComponent.receiveUpload(upstream); + } catch (UploadException e) { + // error happened while receiving file. Handle the + // error in the same manner as it would have + // happened in + // variable change. + synchronized (application) { + handleChangeVariablesError(application, + uploadComponent, e, + new HashMap<String, Object>()); + } } } + } } } catch (final FileUploadException e) { diff --git a/src/com/vaadin/ui/DragAndDropWrapper.java b/src/com/vaadin/ui/DragAndDropWrapper.java index 3274b60b47..a848d7af40 100644 --- a/src/com/vaadin/ui/DragAndDropWrapper.java +++ b/src/com/vaadin/ui/DragAndDropWrapper.java @@ -3,8 +3,11 @@ */ package com.vaadin.ui; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; import java.util.Map; -import java.util.Set; import com.vaadin.event.Transferable; import com.vaadin.event.TransferableImpl; @@ -15,10 +18,13 @@ import com.vaadin.event.dd.DropTargetDetails; import com.vaadin.event.dd.DropTargetDetailsImpl; import com.vaadin.terminal.PaintException; import com.vaadin.terminal.PaintTarget; +import com.vaadin.terminal.UploadStream; import com.vaadin.terminal.gwt.client.MouseEventDetails; import com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper; import com.vaadin.terminal.gwt.client.ui.dd.HorizontalDropLocation; import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation; +import com.vaadin.terminal.gwt.server.AbstractApplicationServlet; +import com.vaadin.ui.DragAndDropWrapper.WrapperTransferable.Html5File; import com.vaadin.ui.Upload.Receiver; @ClientWidget(VDragAndDropWrapper.class) @@ -27,9 +33,24 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget, public class WrapperTransferable extends TransferableImpl { + private Html5File[] files; + public WrapperTransferable(Component sourceComponent, Map<String, Object> rawVariables) { super(sourceComponent, rawVariables); + Integer fc = (Integer) rawVariables.get("filecount"); + if (fc != null) { + files = new Html5File[fc]; + for (int i = 0; i < fc; i++) { + Html5File file = new Html5File(); + String id = (String) rawVariables.get("fi" + i); + file.name = (String) rawVariables.get("fn" + i); + file.size = (Integer) rawVariables.get("fs" + i); + file.type = (String) rawVariables.get("ft" + i); + files[i] = file; + receivers.put(id, file); + } + } } /** @@ -51,21 +72,28 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget, } public Html5File[] getFiles() { - // TODO Auto-generated method stub - return null; + return files; } public class Html5File { + public String name; + private String id; + private int size; + private Receiver receiver; + private String type; + public String getFileName() { - // TODO Auto-generated method stub - return null; + return name; + } + + public int getFileSize() { + return size; } - // public int getFileSize() { - // // TODO Auto-generated method stub - // return 0; - // } + public String getType() { + return type; + } /** * HTML5 drags are read from client disk with a callback. This and @@ -77,14 +105,15 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget, * implementation writes the file contents as it arrives. */ public void receive(Receiver receiver) { - // TODO Auto-generated method stub - + this.receiver = receiver; } } } + private Map<String, Html5File> receivers = new HashMap<String, Html5File>(); + public class WrapperDropDetails extends DropTargetDetailsImpl { /** @@ -195,13 +224,34 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget, return dragStartMode; } - @Override - public void changeVariables(Object source, Map<String, Object> variables) { - super.changeVariables(source, variables); + /** + * This method should only be used by Vaadin terminal implementation. This + * is not end user api. + * + * TODO should fire progress events + end/succes events like upload + * + * @param upstream + * @param fileId + */ + public void receiveFile(UploadStream upstream, String fileId) { + Html5File file = receivers.get(fileId); + if (file != null && file.receiver != null) { + OutputStream receiveUpload = file.receiver.receiveUpload(file + .getFileName(), "TODO"); + + InputStream stream = upstream.getStream(); + byte[] buf = new byte[AbstractApplicationServlet.MAX_BUFFER_SIZE]; + int bytesRead; + try { + while ((bytesRead = stream.read(buf)) != -1) { + receiveUpload.write(buf, 0, bytesRead); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } - Set<String> keySet = variables.keySet(); - for (String string : keySet) { - // TODO get files } + } } |