/** * Copyright (c) 2011-2012, Thilo Planz. All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package v7cr.vaadin; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.logging.Level; import java.util.logging.Logger; import org.vaadin.easyuploads.DirectoryFileFactory; import org.vaadin.easyuploads.FileBuffer; import org.vaadin.easyuploads.FileFactory; import org.vaadin.easyuploads.MultiUploadHandler; import org.vaadin.easyuploads.MultiUpload.FileDetail; import org.vaadin.easyuploads.UploadField.FieldType; import com.vaadin.event.dd.DragAndDropEvent; import com.vaadin.event.dd.DropHandler; import com.vaadin.event.dd.acceptcriteria.AcceptAll; import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; import com.vaadin.terminal.StreamVariable; import com.vaadin.terminal.StreamVariable.StreamingEndEvent; import com.vaadin.terminal.StreamVariable.StreamingErrorEvent; import com.vaadin.terminal.StreamVariable.StreamingProgressEvent; import com.vaadin.terminal.StreamVariable.StreamingStartEvent; import com.vaadin.terminal.gwt.server.AbstractWebApplicationContext; import com.vaadin.terminal.gwt.server.WebBrowser; import com.vaadin.ui.Component; import com.vaadin.ui.CssLayout; import com.vaadin.ui.DragAndDropWrapper; import com.vaadin.ui.Html5File; import com.vaadin.ui.Label; import com.vaadin.ui.ProgressIndicator; import com.vaadin.ui.DragAndDropWrapper.WrapperTransferable; /** * Hacked version of the Vaadin MultiFileUpload, to make it use v7files in proxy * mode: the component posts to v7files, v7files proxies to the streamvariable * */ @SuppressWarnings("serial") public abstract class V7MultiFileUpload extends CssLayout implements DropHandler { private final CssLayout progressBars = new CssLayout(); private final CssLayout uploads = new CssLayout(); private String uploadButtonCaption = "..."; public V7MultiFileUpload() { setWidth("200px"); addComponent(progressBars); uploads.setStyleName("v-multifileupload-uploads"); addComponent(uploads); prepareUpload(); } private void prepareUpload() { final FileBuffer receiver = createReceiver(); final V7MultiUpload upload = new V7MultiUpload(); MultiUploadHandler handler = new MultiUploadHandler() { private LinkedList<ProgressIndicator> indicators; public void streamingStarted(StreamingStartEvent event) { } public void streamingFinished(StreamingEndEvent event) { if (!indicators.isEmpty()) { progressBars.removeComponent(indicators.remove(0)); } File file = receiver.getFile(); handleFile(file, event.getFileName(), event.getMimeType(), event.getBytesReceived()); receiver.setValue(null); } public void streamingFailed(StreamingErrorEvent event) { Logger.getLogger(getClass().getName()).log(Level.FINE, "Streaming failed", event.getException()); for (ProgressIndicator progressIndicator : indicators) { progressBars.removeComponent(progressIndicator); } } public void onProgress(StreamingProgressEvent event) { long readBytes = event.getBytesReceived(); long contentLength = event.getContentLength(); float f = (float) readBytes / (float) contentLength; indicators.get(0).setValue(f); } public OutputStream getOutputStream() { FileDetail next = upload.getPendingFileNames().iterator() .next(); return receiver.receiveUpload(next.getFileName(), next .getMimeType()); } public void filesQueued(Collection<FileDetail> pendingFileNames) { if (indicators == null) { indicators = new LinkedList<ProgressIndicator>(); } for (FileDetail f : pendingFileNames) { ProgressIndicator pi = createProgressIndicator(); progressBars.addComponent(pi); pi.setCaption(f.getFileName()); pi.setVisible(true); indicators.add(pi); } } }; upload.setHandler(handler); upload.setButtonCaption(getUploadButtonCaption()); uploads.addComponent(upload); } private ProgressIndicator createProgressIndicator() { ProgressIndicator progressIndicator = new ProgressIndicator(); progressIndicator.setPollingInterval(300); progressIndicator.setValue(0); return progressIndicator; } public String getUploadButtonCaption() { return uploadButtonCaption; } public void setUploadButtonCaption(String uploadButtonCaption) { this.uploadButtonCaption = uploadButtonCaption; Iterator<Component> componentIterator = uploads.getComponentIterator(); while (componentIterator.hasNext()) { Component next = componentIterator.next(); if (next instanceof V7MultiUpload) { V7MultiUpload upload = (V7MultiUpload) next; if (upload.isVisible()) { upload.setButtonCaption(getUploadButtonCaption()); } } } } private FileFactory fileFactory; public FileFactory getFileFactory() { if (fileFactory == null) { fileFactory = new TempFileFactory(); } return fileFactory; } static class TempFileFactory implements FileFactory { public File createFile(String fileName, String mimeType) { final String tempFileName = "upload_tmpfile_" + System.currentTimeMillis(); try { return File.createTempFile(tempFileName, null); } catch (IOException e) { throw new RuntimeException(e); } } } public void setFileFactory(FileFactory fileFactory) { this.fileFactory = fileFactory; } protected FileBuffer createReceiver() { FileBuffer receiver = new FileBuffer(FieldType.FILE) { @Override public FileFactory getFileFactory() { return V7MultiFileUpload.this.getFileFactory(); } }; return receiver; } protected int getPollinInterval() { return 500; } @Override public void attach() { super.attach(); if (supportsFileDrops()) { prepareDropZone(); } } private DragAndDropWrapper dropZone; /** * Sets up DragAndDropWrapper to accept multi file drops. */ private void prepareDropZone() { if (dropZone == null) { Component label = new Label(getAreaText(), Label.CONTENT_XHTML); label.setSizeUndefined(); dropZone = new DragAndDropWrapper(label); dropZone.setStyleName("v-multifileupload-dropzone"); dropZone.setSizeUndefined(); addComponent(dropZone, 1); dropZone.setDropHandler(this); addStyleName("no-horizontal-drag-hints"); addStyleName("no-vertical-drag-hints"); } } protected String getAreaText() { return "<small>DROP<br/>FILES</small>"; } protected boolean supportsFileDrops() { AbstractWebApplicationContext context = (AbstractWebApplicationContext) getApplication() .getContext(); WebBrowser browser = context.getBrowser(); if (browser.isChrome()) { return true; } else if (browser.isFirefox()) { return true; } else if (browser.isSafari()) { return true; } return false; } abstract protected void handleFile(File file, String fileName, String mimeType, long length); /** * A helper method to set DirectoryFileFactory with given pathname as * directory. * * @param file */ public void setRootDirectory(String directoryWhereToUpload) { setFileFactory(new DirectoryFileFactory( new File(directoryWhereToUpload))); } public AcceptCriterion getAcceptCriterion() { // TODO accept only files // return new And(new TargetDetailIs("verticalLocation","MIDDLE"), new // TargetDetailIs("horizontalLoction", "MIDDLE")); return AcceptAll.get(); } public void drop(DragAndDropEvent event) { DragAndDropWrapper.WrapperTransferable transferable = (WrapperTransferable) event .getTransferable(); Html5File[] files = transferable.getFiles(); for (final Html5File html5File : files) { final ProgressIndicator pi = new ProgressIndicator(); pi.setCaption(html5File.getFileName()); progressBars.addComponent(pi); final FileBuffer receiver = createReceiver(); html5File.setStreamVariable(new StreamVariable() { private String name; private String mime; public OutputStream getOutputStream() { return receiver.receiveUpload(name, mime); } public boolean listenProgress() { return true; } public void onProgress(StreamingProgressEvent event) { float p = (float) event.getBytesReceived() / (float) event.getContentLength(); pi.setValue(p); } public void streamingStarted(StreamingStartEvent event) { name = event.getFileName(); mime = event.getMimeType(); } public void streamingFinished(StreamingEndEvent event) { progressBars.removeComponent(pi); handleFile(receiver.getFile(), html5File.getFileName(), html5File.getType(), html5File.getFileSize()); receiver.setValue(null); } public void streamingFailed(StreamingErrorEvent event) { progressBars.removeComponent(pi); } public boolean isInterrupted() { return false; } }); } } }