瀏覽代碼

Streaming upload and client side implementation for upload + progressindicator

svn changeset:2381/svn branch:trunk
tags/6.7.0.beta1
Matti Tahvonen 16 年之前
父節點
當前提交
749b99fd46

+ 1
- 1
src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java 查看文件

@@ -74,7 +74,7 @@ public class ApplicationConnection implements FocusListener {
return re.test(uri);
}-*/;

private native String getAppUri()/*-{
public native String getAppUri()/*-{
return $wnd.itmtk.appUri;
}-*/;


+ 7
- 0
src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java 查看文件

@@ -20,6 +20,7 @@ import com.itmill.toolkit.terminal.gwt.client.ui.IOrderedLayoutVertical;
import com.itmill.toolkit.terminal.gwt.client.ui.IPanel;
import com.itmill.toolkit.terminal.gwt.client.ui.IPasswordField;
import com.itmill.toolkit.terminal.gwt.client.ui.IPopupCalendar;
import com.itmill.toolkit.terminal.gwt.client.ui.IProgressIndicator;
import com.itmill.toolkit.terminal.gwt.client.ui.IScrollTable;
import com.itmill.toolkit.terminal.gwt.client.ui.ISelect;
import com.itmill.toolkit.terminal.gwt.client.ui.ISlider;
@@ -151,7 +152,11 @@ public class DefaultWidgetSet implements WidgetSet {
} else if ("com.itmill.toolkit.terminal.gwt.client.ui.IFilterSelect"
.equals(className)) {
return new IFilterSelect();
} else if ("com.itmill.toolkit.terminal.gwt.client.ui.IProgressIndicator"
.equals(className)) {
return new IProgressIndicator();
}
return new IUnknownComponent();

/*
@@ -241,6 +246,8 @@ public class DefaultWidgetSet implements WidgetSet {
return "com.itmill.toolkit.terminal.gwt.client.ui.ISplitPanelHorizontal";
} else if ("vsplitpanel".equals(tag)) {
return "com.itmill.toolkit.terminal.gwt.client.ui.ISplitPanelVertical";
} else if ("progressindicator".equals(tag)) {
return "com.itmill.toolkit.terminal.gwt.client.ui.IProgressIndicator";
}

return "com.itmill.toolkit.terminal.gwt.client.ui.IUnknownComponent";

+ 54
- 0
src/com/itmill/toolkit/terminal/gwt/client/ui/IProgressIndicator.java 查看文件

@@ -0,0 +1,54 @@
package com.itmill.toolkit.terminal.gwt.client.ui;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Widget;
import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
import com.itmill.toolkit.terminal.gwt.client.Paintable;
import com.itmill.toolkit.terminal.gwt.client.UIDL;

public class IProgressIndicator extends Widget implements Paintable {
private static final String CLASSNAME = "i-progressindicator";
Element wrapper = DOM.createDiv();
Element indicator = DOM.createDiv();
private ApplicationConnection client;
private Poller poller;
public IProgressIndicator() {
setElement(wrapper);
setStyleName(CLASSNAME);
DOM.appendChild(wrapper, indicator);
poller = new Poller();
}

public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
poller.cancel();
this.client = client;
if(client.updateComponent(this, uidl, true))
return;
boolean indeterminate = uidl.getBooleanAttribute("indeterminate");
if(indeterminate) {
// TODO put up some image or something
} else {
try {
float f = Float.parseFloat(uidl.getStringAttribute("state"));
int size = Math.round(100*f);
DOM.setStyleAttribute(indicator, "width", size + "%");
} catch (Exception e) {
}
}
poller.scheduleRepeating(uidl.getIntAttribute("pollinginterval"));
}
class Poller extends Timer {

public void run() {
client.sendPendingVariableChanges();
}
}
}

+ 81
- 26
src/com/itmill/toolkit/terminal/gwt/client/ui/IUpload.java 查看文件

@@ -1,5 +1,6 @@
package com.itmill.toolkit.terminal.gwt.client.ui;

import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.FileUpload;
@@ -8,58 +9,68 @@ import com.google.gwt.user.client.ui.FormHandler;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.FormSubmitCompleteEvent;
import com.google.gwt.user.client.ui.FormSubmitEvent;
import com.google.gwt.user.client.ui.Frame;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget;
import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
import com.itmill.toolkit.terminal.gwt.client.Paintable;
import com.itmill.toolkit.terminal.gwt.client.UIDL;

public class IUpload extends FormPanel implements Paintable, ClickListener {
public class IUpload extends FormPanel implements Paintable, ClickListener,
FormHandler {

/**
* FileUpload component that opens native OS dialog to select file.
*/
FileUpload fu = new FileUpload();
Panel panel = new FlowPanel();
ApplicationConnection client;

private String paintableId;

/**
* Button that initiates uploading
*/
private Button b;

/**
* When expecting big files, programmer may initiate some UI changes when
* uploading the file starts. Bit after submitting file we'll visit the
* server to check possible changes.
*/
private Timer t;

/**
* some browsers tries to send form twice if submit is called in button
* click handler, some don't submit at all without it, so we need to track
* if form is already being submitted
*/
private boolean submitted = false;

public IUpload() {
super();
setEncoding(FormPanel.ENCODING_MULTIPART);
setMethod(FormPanel.METHOD_POST);
setMethod(FormPanel.METHOD_POST);

setWidget(panel);
panel.add(new Label("UPLOAD component incomplete"));
panel.add(fu);
b = new Button("Upload");
// TODO
b = new Button("Click to Upload");
b.addClickListener(this);
panel.add(b);
addFormHandler(new FormHandler() {
public void onSubmitComplete(FormSubmitCompleteEvent event) {
if(client != null) {
// request update
client.sendPendingVariableChanges();
}
}

public void onSubmit(FormSubmitEvent event) {
if (fu.getFilename().length() == 0) {
event.setCancelled(true);
}
}
});

addFormHandler(this);
}

public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
this.client = client;
this.paintableId = uidl.getId();
if(uidl.hasAttribute("caption"))
setAction(client.getAppUri());

if (uidl.hasAttribute("caption"))
b.setText(uidl.getStringAttribute("caption"));
fu.setName(paintableId + "_file");

}

@@ -67,5 +78,49 @@ public class IUpload extends FormPanel implements Paintable, ClickListener {
this.submit();
}

public void onSubmit(FormSubmitEvent event) {
if (fu.getFilename().length() == 0 || submitted) {
event.setCancelled(true);
ApplicationConnection.getConsole().log(
"Submit cancelled (no file or already submitted)");
return;
}
submitted = true;
ApplicationConnection.getConsole().log("Submitted form");
disableUpload();

/*
* visit server after upload to see possible changes from UploadStarted
* event
*/
t = new Timer() {
public void run() {
client.sendPendingVariableChanges();
}
};
t.schedule(800);
}

protected void disableUpload() {
b.setEnabled(false);
fu.setVisible(false);
}
protected void enableUploaod() {
b.setEnabled(true);
fu.setVisible(true);
}

public void onSubmitComplete(FormSubmitCompleteEvent event) {
if (client != null) {
if (t != null)
t.cancel();
ApplicationConnection.getConsole().log("Submit complete");
client.sendPendingVariableChanges();
}
submitted = false;
enableUploaod();
}

}

+ 8
- 0
src/com/itmill/toolkit/terminal/gwt/public/default/progressindicator/progressindicator.css 查看文件

@@ -0,0 +1,8 @@
.i-progressindicator {
background: red;
height: 20px;
}
.i-progressindicator div {
background: green;
height:100%;
}

+ 1
- 0
src/com/itmill/toolkit/terminal/gwt/public/default/styles.css 查看文件

@@ -12,4 +12,5 @@
@import "tree/tree.css";
@import "splitpanel/splitpanel.css";
@import "select/filterselect.css";
@import "progressindicator/progressindicator.css";

+ 9
- 6
src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java 查看文件

@@ -52,6 +52,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.xml.sax.SAXException;

import com.itmill.toolkit.Application;
@@ -309,6 +310,13 @@ public class ApplicationServlet extends HttpServlet {
Application application = null;
try {

// handle file upload if multipart request
if(ServletFileUpload.isMultipartContent(request)) {
application = getApplication(request);
getApplicationManager(application).handleFileUpload(request, response);
return;
}

// Update browser details
WebBrowser browser = WebApplicationContext.getApplicationContext(
request.getSession()).getBrowser();
@@ -330,11 +338,7 @@ public class ApplicationServlet extends HttpServlet {
// Is this a download request from application
DownloadStream download = null;

// The rest of the process is synchronized with the application
// in order to guarantee that no parallel variable handling is
// made
synchronized (application) {

// Handles AJAX UIDL requests
String resourceId = request.getPathInfo();
if (resourceId != null && resourceId.startsWith(AJAX_UIDL_URI)) {
@@ -385,7 +389,6 @@ public class ApplicationServlet extends HttpServlet {

writeAjaxPage(request, response, window, themeName);
}
}

// For normal requests, transform the window
if (download != null)

+ 142
- 30
src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java 查看文件

@@ -59,16 +59,25 @@ import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;

import com.itmill.toolkit.Application;
import com.itmill.toolkit.Application.WindowAttachEvent;
import com.itmill.toolkit.Application.WindowDetachEvent;
import com.itmill.toolkit.terminal.DownloadStream;
import com.itmill.toolkit.terminal.Paintable;
import com.itmill.toolkit.terminal.URIHandler;
import com.itmill.toolkit.terminal.UploadStream;
import com.itmill.toolkit.terminal.VariableOwner;
import com.itmill.toolkit.terminal.Paintable.RepaintRequestEvent;
import com.itmill.toolkit.ui.Component;
import com.itmill.toolkit.ui.FrameWindow;
import com.itmill.toolkit.ui.Upload;
import com.itmill.toolkit.ui.Window;

/**
@@ -135,10 +144,93 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
application.removeListener((Application.WindowDetachListener) this);
}

/**
* Handles file upload request submitted via Upload component.
*
* @param request
* @param response
* @throws IOException
*/
public void handleFileUpload(HttpServletRequest request,
HttpServletResponse response) throws IOException {
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload();

UploadProgressListener pl = new UploadProgressListener();

upload.setProgressListener(pl);

// Parse the request
FileItemIterator iter;

try {
iter = upload.getItemIterator(request);
/* ATM this loop is run only once as we are uploading one file per
* request.
*/
while (iter.hasNext()) {
FileItemStream item = iter.next();
String name = item.getFieldName();
final String filename = item.getName();
final String mimeType = item.getContentType();
final InputStream stream = item.openStream();
if (item.isFormField()) {
// ignored, upload requests contian only files
} else {
String pid = name.split("_")[0];
Upload uploadComponent = (Upload) idPaintableMap.get(pid);
if (uploadComponent == null) {
throw new FileUploadException(
"Upload component not found");
}
synchronized (application) {
// put upload component into receiving state
uploadComponent.startUpload();
}
UploadStream upstream = new UploadStream() {

public String getContentName() {
return filename;
}

public String getContentType() {
return mimeType;
}

public InputStream getStream() {
return stream;
}

public String getStreamName() {
return "stream";
}

};

// tell UploadProgressListener which component is receiving file
pl.setUpload(uploadComponent);
uploadComponent.receiveUpload(upstream);
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}

// Send short response to acknowledge client that request was done
response.setContentType("text/html");
OutputStream out = response.getOutputStream();
PrintWriter outWriter = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(out, "UTF-8")));
outWriter.print("<html><body>download handled</body></html>");
outWriter.flush();
out.close();
}

public void handleUidlRequest(HttpServletRequest request,
HttpServletResponse response) throws IOException {

// repaint requested or sesssion has timed out and new one is created
// repaint requested or session has timed out and new one is created
boolean repaintAll = (request.getParameter(GET_PARAM_REPAINT_ALL) != null)
|| request.getSession().isNew();

@@ -146,10 +238,6 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
PrintWriter outWriter = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(out, "UTF-8")));

// TODO Move dirt elsewhere
outWriter.print(")/*{"); // some dirt to prevent cross site scripting
// vulnerabilities

try {

// Is this a download request from application
@@ -193,6 +281,9 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,

// Sets the response type
response.setContentType("application/json; charset=UTF-8");
// some dirt to prevent cross site scripting
outWriter.print(")/*{");

outWriter.print("\"changes\":[");

paintTarget = new JsonPaintTarget(this, outWriter);
@@ -206,7 +297,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
// Reset sent locales
locales = null;
requireLocale(application.getLocale().toString());
} else
paintables = getDirtyComponents();
if (paintables != null) {
@@ -267,17 +358,14 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
w.setTerminal(application.getMainWindow()
.getTerminal());
}
/* This does not seem to happen in tk5, but remember this case:
else if (p instanceof Component) {
if (((Component) p).getParent() == null
|| ((Component) p).getApplication() == null) {
// Component requested repaint, but is no
// longer attached: skip
paintablePainted(p);
continue;
}
}
*/
/*
* This does not seem to happen in tk5, but remember
* this case: else if (p instanceof Component) { if
* (((Component) p).getParent() == null ||
* ((Component) p).getApplication() == null) { //
* Component requested repaint, but is no // longer
* attached: skip paintablePainted(p); continue; } }
*/
paintTarget.startTag("change");
paintTarget.addAttribute("format", "uidl");
String pid = getPaintableId(p);
@@ -318,8 +406,9 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
if (request.getParameter("theme") != null) {
themeName = request.getParameter("theme");
}
if (themeName == null) themeName = "default";
if (themeName == null)
themeName = "default";

// TODO We should only precache the layouts that are not
// cached already
int resourceIndex = 0;
@@ -413,14 +502,15 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
Map m;
if (i + 2 >= ca.length
|| !vid[0].equals(ca[i + 2].split("_")[0])) {
if(ca.length > i + 1) {
m = new SingleValueMap(vid[1], convertVariableValue(
vid[2].charAt(0), ca[++i]));
if (ca.length > i + 1) {
m = new SingleValueMap(vid[1],
convertVariableValue(vid[2].charAt(0),
ca[++i]));
} else {
m = new SingleValueMap(vid[1], convertVariableValue(vid[2].charAt(0), ""));
m = new SingleValueMap(vid[1],
convertVariableValue(vid[2].charAt(0), ""));
}
}
else {
} else {
m = new HashMap();
m.put(vid[1], convertVariableValue(vid[2].charAt(0),
ca[++i]));
@@ -465,8 +555,9 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
val = Boolean.valueOf(strValue);
break;
}
System.out.println("result: " + val + " of type " + (val == null ? "-" : val.getClass().toString()));

System.out.println("result: " + val + " of type "
+ (val == null ? "-" : val.getClass().toString()));
return val;
}

@@ -605,7 +696,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,

// Find the window where the request is handled
String path = request.getPathInfo();
// Remove UIDL from the path
path = path.substring("/UIDL".length());

@@ -779,8 +870,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
new OutputStreamWriter(out, "UTF-8")));
outWriter.print(")/*{");
outWriter.print("\"redirect\":{");
outWriter.write("\"url\":\"" + logoutUrl
+ "\"}");
outWriter.write("\"url\":\"" + logoutUrl + "\"}");
outWriter.flush();
outWriter.close();
out.flush();
@@ -1067,4 +1157,26 @@ public class CommunicationManager implements Paintable.RepaintRequestListener,
else
return new Locale(temp[0], temp[1], temp[2]);
}

/*
* Upload progress listener notifies upload component once when Jakarta
* FileUpload can determine content length. Used to detect files total size,
* uploads progress can be tracked inside upload.
*/
private class UploadProgressListener implements ProgressListener {
Upload uploadComponent;
boolean updated = false;

public void setUpload(Upload u) {
uploadComponent = u;
}

public void update(long bytesRead, long contentLength, int items) {
if (!updated && uploadComponent != null) {
uploadComponent.setUploadSize(contentLength);
updated = true;
}
}
}

}

+ 207
- 0
src/com/itmill/toolkit/tests/TestForUpload.java 查看文件

@@ -0,0 +1,207 @@
package com.itmill.toolkit.tests;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import com.itmill.toolkit.terminal.StreamResource;
import com.itmill.toolkit.ui.Button;
import com.itmill.toolkit.ui.CustomComponent;
import com.itmill.toolkit.ui.Label;
import com.itmill.toolkit.ui.Layout;
import com.itmill.toolkit.ui.Link;
import com.itmill.toolkit.ui.OrderedLayout;
import com.itmill.toolkit.ui.Panel;
import com.itmill.toolkit.ui.ProgressIndicator;
import com.itmill.toolkit.ui.Upload;
import com.itmill.toolkit.ui.Button.ClickEvent;
import com.itmill.toolkit.ui.Upload.FailedEvent;
import com.itmill.toolkit.ui.Upload.FailedListener;
import com.itmill.toolkit.ui.Upload.FinishedEvent;
import com.itmill.toolkit.ui.Upload.FinishedListener;
import com.itmill.toolkit.ui.Upload.StartedEvent;
import com.itmill.toolkit.ui.Upload.StartedListener;
import com.itmill.toolkit.ui.Upload.SucceededEvent;
import com.itmill.toolkit.ui.Upload.SucceededListener;

public class TestForUpload extends CustomComponent implements
Upload.FinishedListener, FailedListener,SucceededListener, Upload.ProgressListener, StartedListener {

Layout main = new OrderedLayout();

Buffer buffer = new Buffer();

Panel status = new Panel("Uploaded file:");

private Upload up;

private Label l;
private ProgressIndicator pi = new ProgressIndicator();

public TestForUpload() {
setCompositionRoot(main);
main.addComponent( new Label(
"This is a simple test for upload application. "
+ "Upload should work with big files and concurrent "
+ "requests should not be blocked. Button 'b' reads "
+ "current state into label below it. TODO make "
+ "streaming example/test where upload contents "
+ "is read but not saved and memory consumption is "
+ "verified low. TODO make test where contents is "
+ "written to disk and verifiy low memory consumption."));
main.addComponent(new Label("Clicking on button b updates information about upload components status."));

up = new Upload("Upload", buffer);
up.setImmediate(true);
up.addListener((FinishedListener)this);
up.addListener((FailedListener) this);
up.addListener((SucceededListener) this);
up.addListener((StartedListener) this);
up.setProgressListener(this);

Button b = new Button("b", this, "readState");

main.addComponent(b);



main.addComponent(up);
l = new Label("Idle");
main.addComponent(l);
pi.setVisible(false);
pi.setPollingInterval(1000);
main.addComponent(pi);

status.setVisible(false);
main.addComponent(status);


Button restart = new Button("R");
restart.addListener(new Button.ClickListener() {

public void buttonClick(ClickEvent event) {
getApplication().close();
}
});
main.addComponent(restart);

}

public void readState() {
StringBuffer sb = new StringBuffer();

if (up.isUploading()) {
sb.append("Uploading...");
sb.append(up.getBytesRead());
sb.append("/");
sb.append(up.getUploadSize());
sb.append(" ");
sb.append(Math.round(100 * up.getBytesRead()
/ (double) up.getUploadSize()));
sb.append("%");
} else {
sb.append("Idle");
}
l.setValue(sb.toString());
}

public void uploadFinished(FinishedEvent event) {
status.removeAllComponents();
if (buffer.getStream() == null)
status.addComponent(new Label(
"Upload finished, but output buffer is null!!"));
else {
status
.addComponent(new Label("<b>Name:</b> "
+ event.getFilename(), Label.CONTENT_XHTML));
status.addComponent(new Label("<b>Mimetype:</b> "
+ event.getMIMEType(), Label.CONTENT_XHTML));
status.addComponent(new Label("<b>Size:</b> " + event.getLength()
+ " bytes.", Label.CONTENT_XHTML));

status.addComponent(new Link("Download " + buffer.getFileName(),
new StreamResource(buffer, buffer.getFileName(),
getApplication())));

status.setVisible(true);
}
}

public class Buffer implements StreamResource.StreamSource, Upload.Receiver {
ByteArrayOutputStream outputBuffer = null;

String mimeType;

String fileName;

public Buffer() {

}

public InputStream getStream() {
if (outputBuffer == null)
return null;
return new ByteArrayInputStream(outputBuffer.toByteArray());
}

/**
* @see com.itmill.toolkit.ui.Upload.Receiver#receiveUpload(String,
* String)
*/
public OutputStream receiveUpload(String filename, String MIMEType) {
fileName = filename;
mimeType = MIMEType;
outputBuffer = new ByteArrayOutputStream();
return outputBuffer;
}

/**
* Returns the fileName.
*
* @return String
*/
public String getFileName() {
return fileName;
}

/**
* Returns the mimeType.
*
* @return String
*/
public String getMimeType() {
return mimeType;
}

}

public void uploadFailed(FailedEvent event) {
System.out.println(event);
System.out.println(event.getSource());
}

public void uploadSucceeded(SucceededEvent event) {
pi.setVisible(false);
l.setValue("Finished upload, idle");
System.out.println(event);
}
public void updateProgress(long readBytes, long contentLenght) {
pi.setValue(new Float(readBytes/(float)contentLenght));
}

public void uploadStarted(StartedEvent event) {
pi.setVisible(true);
l.setValue("Started uploading file " + event.getFilename());
}

}

+ 1
- 1
src/com/itmill/toolkit/ui/ProgressIndicator.java 查看文件

@@ -45,7 +45,7 @@ import com.itmill.toolkit.terminal.PaintTarget;
* @author IT Mill Ltd.
* @version
* @VERSION@
* @since 3.1
* @since 4
*/
public class ProgressIndicator extends AbstractField implements Property,
Property.Viewer, Property.ValueChangeListener {

+ 239
- 24
src/com/itmill/toolkit/ui/Upload.java 查看文件

@@ -71,6 +71,18 @@ public class Upload extends AbstractComponent implements Component.Focusable {

private long focusableId = -1;

private boolean isUploading;

private long contentLength = -1;

private int totalBytes;

/**
* ProgressListener to which information about progress is sent during
* upload
*/
private ProgressListener progressListener;

/* TODO: Add a default constructor, receive to temp file. */

/**
@@ -95,25 +107,16 @@ public class Upload extends AbstractComponent implements Component.Focusable {
return "upload";
}

/**
* Invoked when the value of a variable has changed.
*
* @see com.itmill.toolkit.ui.AbstractComponent#changeVariables(java.lang.Object,
* java.util.Map)
*/
public void changeVariables(Object source, Map variables) {

// Checks the variable name
if (!variables.containsKey("stream"))
return;

// Gets the upload stream
UploadStream upload = (UploadStream) variables.get("stream");
public void receiveUpload(UploadStream upload) {
if (!isUploading)
throw new IllegalStateException("uploading not started");

// Gets file properties
String filename = upload.getContentName();
String type = upload.getContentType();

fireStarted(filename, type);

// Gets the output target stream
OutputStream out = receiver.receiveUpload(filename, type);
if (out == null)
@@ -121,32 +124,53 @@ public class Upload extends AbstractComponent implements Component.Focusable {
"Error getting outputstream from upload receiver");

InputStream in = upload.getStream();

if (null == in) {
// No file, for instance non-existent filename in html upload
fireUploadInterrupted(filename, type, 0);
endUpload();
return;
}

byte buffer[] = new byte[BUFFER_SIZE];
int bytesRead = 0;
long totalBytes = 0;
totalBytes = 0;
try {
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
if (progressListener != null && contentLength > 0) {
// update progress if listener set and contentLength
// received
progressListener.updateProgress(totalBytes, contentLength);
}
}

// Download successfull
// upload successful
out.close();
fireUploadSuccess(filename, type, totalBytes);
endUpload();
requestRepaint();

} catch (IOException e) {

// Download interrupted
fireUploadInterrupted(filename, type, totalBytes);
endUpload();
}
}

/**
* Invoked when the value of a variable has changed.
*
* @see com.itmill.toolkit.ui.AbstractComponent#changeVariables(java.lang.Object,
* java.util.Map)
*/
public void changeVariables(Object source, Map variables) {
// NOP

}

/**
* Paints the content of this component.
*
@@ -164,6 +188,10 @@ public class Upload extends AbstractComponent implements Component.Focusable {
if (this.tabIndex >= 0)
target.addAttribute("tabindex", this.tabIndex);

target.addAttribute("state", isUploading);

target.addVariable(this, "fake", true);

target.addUploadStreamVariable(this, "stream");
}

@@ -198,12 +226,16 @@ public class Upload extends AbstractComponent implements Component.Focusable {

private static final Method UPLOAD_SUCCEEDED_METHOD;

private static final Method UPLOAD_STARTED_METHOD;

static {
try {
UPLOAD_FINISHED_METHOD = FinishedListener.class.getDeclaredMethod(
"uploadFinished", new Class[] { FinishedEvent.class });
UPLOAD_FAILED_METHOD = FailedListener.class.getDeclaredMethod(
"uploadFailed", new Class[] { FailedEvent.class });
UPLOAD_STARTED_METHOD = StartedListener.class.getDeclaredMethod(
"uploadStarted", new Class[] { StartedEvent.class });
UPLOAD_SUCCEEDED_METHOD = SucceededListener.class
.getDeclaredMethod("uploadSucceeded",
new Class[] { SucceededEvent.class });
@@ -282,21 +314,21 @@ public class Upload extends AbstractComponent implements Component.Focusable {
}

/**
* Gets the length of the file.
* Gets the MIME Type of the file.
*
* @return the length.
* @return the MIME type.
*/
public long getLength() {
return length;
public String getMIMEType() {
return type;
}

/**
* Gets the MIME Type of the file.
* Gets the length of the file.
*
* @return the MIME type.
* @return the length.
*/
public String getMIMEType() {
return type;
public long getLength() {
return length;
}

}
@@ -360,6 +392,85 @@ public class Upload extends AbstractComponent implements Component.Focusable {

}

/**
* Upload.Started event is sent when the upload is started to received.
*
* @author IT Mill Ltd.
* @version
* @VERSION@
* @since 5.0
*/
public class StartedEvent extends Component.Event {

/**
* Serial generated by eclipse.
*/
private static final long serialVersionUID = -3984393770487403525L;
private String filename;
private String type;

/**
*
* @param source
* @param filename
* @param MIMEType
* @param length
*/
public StartedEvent(Upload source, String filename, String MIMEType) {
super(source);
this.filename = filename;
this.type = MIMEType;
}
/**
* Uploads where the event occurred.
*
* @return the Source of the event.
*/
public Upload getUpload() {
return (Upload) getSource();
}

/**
* Gets the file name.
*
* @return the filename.
*/
public String getFilename() {
return filename;
}

/**
* Gets the MIME Type of the file.
*
* @return the MIME type.
*/
public String getMIMEType() {
return type;
}


}

/**
* Receives the events when the upload starts.
*
* @author IT Mill Ltd.
* @version
* @VERSION@
* @since 5.0
*/
public interface StartedListener {

/**
* Upload has started.
*
* @param event
* the Upload started event.
*/
public void uploadStarted(StartedEvent event);
}

/**
* Receives the events when the uploads are ready.
*
@@ -417,6 +528,26 @@ public class Upload extends AbstractComponent implements Component.Focusable {
public void uploadSucceeded(SucceededEvent event);
}

/**
* Adds the upload started event listener.
*
* @param listener
* the Listener to be added.
*/
public void addListener(StartedListener listener) {
addListener(StartedEvent.class, listener, UPLOAD_STARTED_METHOD);
}

/**
* Removes the upload started event listener.
*
* @param listener
* the Listener to be removed.
*/
public void removeListener(StartedListener listener) {
removeListener(FinishedEvent.class, listener, UPLOAD_STARTED_METHOD);
}

/**
* Adds the upload received event listener.
*
@@ -477,6 +608,17 @@ public class Upload extends AbstractComponent implements Component.Focusable {
removeListener(SucceededEvent.class, listener, UPLOAD_SUCCEEDED_METHOD);
}

/**
* Emit upload received event.
*
* @param filename
* @param MIMEType
* @param length
*/
protected void fireStarted(String filename, String MIMEType) {
fireEvent(new Upload.StartedEvent(this, filename, MIMEType));
}

/**
* Emit upload received event.
*
@@ -572,4 +714,77 @@ public class Upload extends AbstractComponent implements Component.Focusable {
return this.focusableId;
}

/**
* Sets the size of the file currently being uploaded.
*
* @param contentLength
*/
public void setUploadSize(long contentLength) {
this.contentLength = contentLength;
}

/**
* Go into upload state. This is to prevent double uploading on same
* component.
*/
public void startUpload() {
if (isUploading)
throw new IllegalStateException("uploading already started");
isUploading = true;
}

/**
* Go into state where new uploading can begin.
*/
public void endUpload() {
isUploading = false;
contentLength = -1;
}

public boolean isUploading() {
return isUploading;
}

/**
* Gets read bytes of the file currently being uploaded.
*
* @return bytes
*/
public long getBytesRead() {
return totalBytes;
}

/**
* Returns size of file currently being uploaded. Value sane only during
* upload.
*
* @return size in bytes
*/
public long getUploadSize() {
return contentLength;
}

/**
* Sets listener to track progress of upload.
*
* @param progressListener
*/
public void setProgressListener(ProgressListener progressListener) {
this.progressListener = progressListener;
}

/**
* ProgressListener receives events to track progress of upload.
*/
public interface ProgressListener {
/**
* Updates progress to listener
*
* @param readBytes
* bytes transferred
* @param contentLength
* total size of file currently being uploaded, -1 if unknown
*/
public void updateProgress(long readBytes, long contentLength);
}
}

Loading…
取消
儲存