Usually I use FileDownloader related to the button. I mean, I have a button to download a file, and when I click on it, I download the file.
Now I have a button which starts an export in csv format. I would, in the end of this process, download to the user, automatically (without button click), the file just created.
It will not work that way, as you are creating the FileDownloader in the button click event handler.
Instead you should extend the button outside of the click handler, as it is given in the example using somehting like:
Try creating the FileDownloader outside the buttonClick event instead and extend the button right after you’ve created the button. Now you are creating the FileDownloader when the click has already happened.
/** This specializes {@link FileDownloader} in a way,
* such that both the file name and content can be determined
* on-demand, i.e. when the user has clicked the component. */
// now create the file downloader with the stream resource and extend the button
FileDownloader fileDownloader = new FileDownloader(downloadResource);
fileDownloader.extend(export);
[/code]I haven’t run this code, let me know how it goes.
Would this work for you?
Basically you delay the StreamResource creation until the download has triggerd by the client.
Of course products has to be effectively final for this to work.
You can’t create the download after the fact and trigger it programmatically, browsers these days prevent that, there is pretty much no way around it.
You
can go the suggested lazy route, so that the download is triggered automatically right away when the click happens, and effectively just be so slow about it that you have time to generate the file on the fly. It seems trickier at the first glance, but don’t be discouraged by that – the lazy route works.
Yes, that should work. You specify the stream and filedownloader when you create the view, and you create the file within you getStream() method within your StreamResource.
I solved this issue creating a foo resource method for the creation of FileDownloader, then, for listener event, I set the “true” resource method. Make sense for you?
final FileDownloader fileDownloader = new FileDownloader(
new StreamResource(new StreamResource.StreamSource() {
public InputStream getStream() {
return null;
}
}, "")
);
final Button myButton = new Button("Download file", listener -> {
fileDownloader
.setFileDownloadResource(createAwesomeCSVResource());
});
fileDownloader.extend(myButton);
How to use a button to generate a file and download it in the same action event
The solution from above did not work for me (the listener was triggered after download started).
To solve the problem I needed to create DynamicFileResource with a reflection hack (because Vaadin team decided to make the method FileResource.setSourceFile(File sourceFile) private).
@SuppressWarnings("serial")
public static class DynamicFileResource extends FileResource {
protected Callable<String> processor;
/** provide the source file using callback */
public DynamicFileResource(Callable<String> processor) {
super(new File("."));
this.processor = processor;
}
@Override
public DownloadStream getStream() {
try {
// process and set the resulted file before returning stream
String filePath = processor.call();
setSourceFile(new File(filePath));
} catch (Exception e) {
throw new IllegalStateException("Unable to generate file resource", e);
}
return super.getStream();
}
// set sourceFile (maybe Vaadin team should make the method setSourceFile(File sourceFile) public..)
public void setSourceFile(File sourceFile) {
// access private member: FileResource.sourceFile
try {
Field f = FileResource.class.getDeclaredField("sourceFile");
f.setAccessible(true); // force accessible
f.set(this, sourceFile);
} catch (Exception e) {
throw new IllegalStateException("Unable to set sourceFile", e);
}
}
}
public enum EXPORT_TYPE {
CSV, EXCEL, PDF
}
/** generated and download report */
private void attachReportExporter(Button button, final EXPORT_TYPE exportType) {
FileDownloader fileDownloader = new FileDownloader(new DynamicFileResource(() -> generateReport(exportType)));
fileDownloader.extend(button);
}
private String generateReport(EXPORT_TYPE exportType) {
// generate the report and return the path to resulted file
...
return filePath;
}
// sample usage attachExportReport()
private void createLayout() {
btnExportCsv = new Button("Export Csv");
attachReportExporter(btnExportCsv, EXPORT_TYPE.CSV);
btnExportExcel = new Button("Export Excel");
attachReportExporter(btnExportExcel, EXPORT_TYPE.EXCEL);
btnExportPdf = new Button("Export PDF");
attachReportExporter(btnExportPdf, EXPORT_TYPE.PDF);
}