5

I try to add button with function to download the file from external resource

Button saveAsButton = new Button();
private FileDownloader fileDownloader;
fileDownloader = new FileDownloader(new ExternalResource(........));
fileDownloader.extend(saveAsButton);

This doesn't work for me :/ no errors in the background

PDS
  • 562
  • 3
  • 13
  • 27

3 Answers3

6

Referred from this link. A custom file downloader can give you this functionality. Here is the custom class and demo. The main advantage of using this AdvancedFileDownloader over inbuilt com.vaadin.server.FileDownloader is that you can change file path on each button click. In FileDownloader, once a file is set, it cannot be changed i.e. a button can download only the same file on each click.

AdvancedFileDownloader.java :-

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Logger;

import com.vaadin.server.ConnectorResource;
import com.vaadin.server.DownloadStream;
import com.vaadin.server.FileDownloader;
import com.vaadin.server.Resource;
import com.vaadin.server.StreamResource;
import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinSession;
import com.vaadin.server.StreamResource.StreamSource;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinResponse;
import com.vaadin.ui.AbstractComponent;

/**
 * an advanced file downloader
 *
 * @author visruth
 *
 */
public class AdvancedFileDownloader extends FileDownloader {

    /**
     *
     */
    private static final long serialVersionUID = 7914516170514586601L;
    private static final boolean DEBUG_MODE = true;

    private static final Logger logger = java.util.logging.Logger
            .getLogger(AdvancedFileDownloader.class.getName());

    private AbstractComponent extendedComponet;

    private AdvancedDownloaderListener dynamicDownloaderListener;
    private DownloaderEvent downloadEvent;

    public abstract class DownloaderEvent {

        /**
         *
         * @return
         */
        public abstract AbstractComponent getExtendedComponet();

        public abstract void setExtendedComponet(
                AbstractComponent extendedComponet);

    }

    public interface AdvancedDownloaderListener {
        /**
         * This method will be invoked just before the download starts. Thus, a
         * new file path can be set.
         *
         * @param downloadEvent
         */
        public void beforeDownload(DownloaderEvent downloadEvent);
    }

    public void fireEvent() {
        if (DEBUG_MODE) {
            logger.info("inside fireEvent");
        }
        if (this.dynamicDownloaderListener != null
                && this.downloadEvent != null) {
            if (DEBUG_MODE) {
                logger.info("beforeDownload is going to be invoked");
            }
            this.dynamicDownloaderListener.beforeDownload(this.downloadEvent);
        }
    }

    public void addAdvancedDownloaderListener(
            AdvancedDownloaderListener listener) {
        if (listener != null) {
            DownloaderEvent downloadEvent = new DownloaderEvent() {

                private AbstractComponent extendedComponet;

                @Override
                public void setExtendedComponet(
                        AbstractComponent extendedComponet) {
                    this.extendedComponet = extendedComponet;
                }

                @Override
                public AbstractComponent getExtendedComponet() {
                    // TODO Auto-generated method stub
                    return this.extendedComponet;
                }
            };
            downloadEvent
                    .setExtendedComponet(AdvancedFileDownloader.this.extendedComponet);
            this.dynamicDownloaderListener = listener;
            this.downloadEvent = downloadEvent;

        }
    }

    private static class FileResourceUtil {

        private String filePath;

        private String fileName = "";

        private File file;

        public String getFilePath() {
            return filePath;
        }

        public void setFilePath(String filePath) {
            this.filePath = filePath;
            file = new File(filePath);

            if (file.exists() && !file.isDirectory()) {
                fileName = file.getName();
            }
        }

        /**
         * makes a stream resource
         *
         * @return {@code StreamResource}
         */
        @SuppressWarnings("serial")
        public StreamResource getResource() {
            return new StreamResource(new StreamSource() {

                @Override
                public InputStream getStream() {

                    if (filePath != null && file != null) {

                        if (file.exists() && !file.isDirectory()) {
                            try {
                                return new FileInputStream(file);
                            } catch (FileNotFoundException e) {
                                e.printStackTrace();
                                return null;
                            }
                        } else {
                            return null;
                        }

                    }
                    return null;
                }

            }, FileResourceUtil.this.fileName) {
                @Override
                public String getFilename() {
                    return FileResourceUtil.this.fileName;
                }

            };
        }

    }

    private FileResourceUtil resource;

    private AdvancedFileDownloader(FileResourceUtil resource) {

        super(resource == null ? (resource = new FileResourceUtil())
                .getResource() : resource.getResource());

        AdvancedFileDownloader.this.resource = resource;
        System.out.println("created a new instance of resource : " + resource);
    }

    public AdvancedFileDownloader() {
        this(null);
    }

    /**
     * @return the current file path
     */
    public String getFilePath() {
        return resource.getFilePath();
    }

    /**
     * sets the path for the file for downloading
     *
     * @param filePath
     *            path of the file, i.e. path + file name with extension
     */
    public void setFilePath(String filePath) {

        if (resource != null && filePath != null) {
            this.resource.setFilePath(filePath);
            ;
        }
    }

    @Override
    public boolean handleConnectorRequest(VaadinRequest request,
            VaadinResponse response, String path) throws IOException {

      
        if (!path.matches("dl(/.*)?")) {
            // Ignore if it isn't for us
            return false;
        }
        VaadinSession session = getSession();

        session.lock();
        AdvancedFileDownloader.this.fireEvent();

        DownloadStream stream;

        try {
            Resource resource = getFileDownloadResource();
            if (!(resource instanceof ConnectorResource)) {
                return false;
            }
            stream = ((ConnectorResource) resource).getStream();

            if (stream.getParameter("Content-Disposition") == null) {
                // Content-Disposition: attachment generally forces download
                stream.setParameter("Content-Disposition",
                        "attachment; filename=\"" + stream.getFileName() + "\"");
            }

            // Content-Type to block eager browser plug-ins from hijacking
            // the file
            if (isOverrideContentType()) {
                stream.setContentType("application/octet-stream;charset=UTF-8");
            }
          
        } finally {
            session.unlock();
        }
        stream.writeResponse(request, response);
        return true;
    }
}

And the demo codo to run. In this code you have to give the file path for downloading the file.

package com.example.samplevaadin;

import javax.servlet.annotation.WebServlet;

import com.example.samplevaadin.ui.componet.custom.AdvancedFileDownloader;
import com.example.samplevaadin.ui.componet.custom.AdvancedFileDownloader.AdvancedDownloaderListener;
import com.example.samplevaadin.ui.componet.custom.AdvancedFileDownloader.DownloaderEvent;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Link;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

@SuppressWarnings("serial")
@Theme("samplevaadin")
public class DemoUI extends UI {

    @WebServlet(value = "/*", asyncSupported = true)
    @VaadinServletConfiguration(productionMode = false, ui = DemoUI.class)
    public static class Servlet extends VaadinServlet {
    }

    @Override
    protected void init(VaadinRequest request) {

        final VerticalLayout layout = new VerticalLayout();
        layout.setMargin(true);

        
        final TextField inputFilepathField = new TextField();
        inputFilepathField.setValue("/home/visruthcv/README.txt");
        inputFilepathField.setImmediate(true);
        layout.addComponent(inputFilepathField);

        Button downloadButton = new Button("Download Button");
        // or
        Link downloadLink = new Link();
        downloadLink.setCaption("Download link");

        final AdvancedFileDownloader downloader = new AdvancedFileDownloader();
        downloader
                .addAdvancedDownloaderListener(new AdvancedDownloaderListener() {

                    /**
                     * This method will be invoked just before the download
                     * starts. Thus, a new file path can be set.
                     * 
                     * @param downloadEvent
                     */
                    @Override
                    public void beforeDownload(DownloaderEvent downloadEvent) {

                        String filePath = inputFilepathField.getValue();

                        downloader.setFilePath(filePath);
                        
                        System.out.println("Starting downlad by button "
                                + filePath.substring(filePath.lastIndexOf("/")));
                    }

                });

        downloader.extend(downloadButton);
        layout.addComponent(downloadButton);

        final AdvancedFileDownloader downloaderForLink = new AdvancedFileDownloader();
        downloaderForLink
                .addAdvancedDownloaderListener(new AdvancedDownloaderListener() {

                    /**
                     * This method will be invoked just before the download
                     * starts. Thus, a new file path can be set.
                     * 
                     * @param downloadEvent
                     */
                    @Override
                    public void beforeDownload(DownloaderEvent downloadEvent) {

                        String filePath = inputFilepathField.getValue();

                        downloaderForLink.setFilePath(filePath);
                        System.out.println("Starting download by link "
                                + filePath.substring(filePath.lastIndexOf("/")));
                        
                    }

                });

        downloaderForLink.extend(downloadLink);
        layout.addComponent(downloadLink);

        setContent(layout);
    }

}

The beforeDownload method will be invoked when you click the download button so that the file path can be changed to download a different file.

Visruth
  • 3,430
  • 35
  • 48
  • I see corrupted download when using the AdvancedDownloader in a VMWare environment. The file is created and stored on server disk. The download to the client then has most of the time 32k / 64k and not 46kB or 77kB. Anyone seen this? – André Schild Mar 18 '15 at 16:58
  • @AndréSchild is it working fine in a normal server environment? Make sure that the path is valid at the time of download. Can you check as :- First try with a sample text file containing more than 64KB of data and try to print the file contents in `beforeDownload` method itself, and verify it prints all of the contents. – Visruth Mar 18 '15 at 18:55
  • Yes, it works on non-vm environments. The file is dynamically generated and stored on disk with Fileoutputstream and out.close() before I pass the filename to download it. – André Schild Mar 18 '15 at 21:27
  • @AndréSchild I think, the path may not be readable from vm environment even if the file is created in the disk, the base path might be different when you access the file from vm environment. And also, ensure that the path/file has the permission to be accessed. – Visruth Mar 19 '15 at 17:20
3

Use a FileResource instead of ExternalResource. In order to offer a file for download, the following has been proven:

Button btn = new Button("Download");
layout.addComponent(btn);

Resource res = new FileResource(new File("/tmp/file.pdf"));
FileDownloader fd = new FileDownloader(res);
fd.extend(btn);
nexus
  • 2,937
  • 3
  • 17
  • 22
  • I use relative path and get error (File not found: download?fileId=3174), I will try absolute path. Anyway when I use BrowserWindowOpener bwo = new BrowserWindowOpener("/download?fileId=" + file.getFileId()); bwo.extend(button); there is no problem to preview – PDS Feb 22 '14 at 20:38
  • @PDave it makes for me no sense to reference a local located file through a fileprovider like you did with "download?..." The FileResource takes a file reference in his constructor! – nexus Feb 22 '14 at 20:57
0

I have two methods and there are some parameters.

void buttonClick(){
       File file = generateReportFile();
        if (file != null) {
            sendConvertedFileToUser(file, branchName + "_" + sdf.format(new Date()) + ".xlsx");
        }
 }

private File generateReportFile() {

        File tempFile = null;
        FileOutputStream fileOut = null;

        try {

            tempFile = File.createTempFile("tmp", ".xlsx");
            fileOut = new FileOutputStream(tempFile);
            workbook.write(fileOut);

        } catch (final IOException e) {
            return null;
        } finally {
            if (tempFile != null) {
                tempFile.deleteOnExit();
            }
            try {
                if (fileOut != null) {
                    fileOut.close();
                }
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }

        return tempFile;
    }



 private boolean sendConvertedFileToUser(final File fileToExport, final String exportFileName) {

        UI ui = UI.getCurrent();
        TemporaryFileDownloadResource resource;
        try {
            resource = new TemporaryFileDownloadResource(ui, exportFileName, this.EXCEL_MIME_TYPE, fileToExport);
            ui.getPage().open(resource, null, false);
        } catch (final FileNotFoundException e) {
            return false;
        }

        return true;
    }
stephan
  • 271
  • 1
  • 4
  • 24