Как использовать потоковый контент с p:fileDownload для загрузки файла без пути к классу
Я использую Primefaces
р: FileDownload
скачать файл, который не находится в пути к классам.
Поэтому я передаю FileInputStream в качестве параметра DefaultStreamedContent.
Все работает нормально, когда мой бин хранится в @SessionScoped...,
Но
java.io.NotSerializableException: java.io.FileInputStream
выбрасывается, когда я держу свой бин в @Viewscoped.
Мой код:
DownloadBean.java
@ManagedBean
@ViewScoped
public class DownloadBean implements Serializable {
private StreamedContent dFile;
public StreamedContent getdFile() {
return dFile;
}
public void setdFile(StreamedContent dFile) {
this.dFile = dFile;
}
/**
* This Method will be called when download link is clicked
*/
public void downloadAction()
{
File tempFile = new File("C:/temp.txt");
try {
dFile = new DefaultStreamedContent(new FileInputStream(tempFile), new MimetypesFileTypeMap().getContentType(tempFile));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
index.xhtml
<h:form>
<h:commandLink action="#{downloadBean.downloadAction}">
Download
<p:fileDownload value="#{downloadBean.dFile}"/>
</h:commandLink>
</h:form>
Нет ли способа заставить его работать?
2 ответа
NotSerializableException
генерируется, потому что область представления представлена состоянием представления JSF, которое, в свою очередь, может быть сериализовано в сеанс HTTP в случае сохранения состояния на стороне сервера или скрытого поля ввода HTML в случае сохранения состояния на стороне клиента. FileInputStream
никоим образом не может быть представлен в сериализованной форме.
Если вам абсолютно необходимо ограничить представление компонента, то вы не должны объявлять StreamedContent
в качестве переменной экземпляра, но вместо этого воссоздайте ее в методе получения. Правда, выполнение бизнес-логики в методе получения обычно не одобряется, но StreamedContent
это довольно особый случай. В методе действия вы должны только подготовить сериализуемые переменные, которые позже будут использоваться во время DefaultStreamedContent
строительство.
@ManagedBean
@ViewScoped
public class DownloadBean implements Serializable {
private String path;
private String contentType;
public void downloadAction() {
path = "C:/temp.txt";
contentType = FacesContext.getCurrentInstance().getExternalContext().getMimeType(path);
}
public StreamedContent getdFile() throws IOException {
return new DefaultStreamedContent(new FileInputStream(path), contentType);
}
}
(обратите внимание, что я также исправил ваш способ получения типа контента; у вас есть гораздо больше свободы для настройки типов MIME через <mime-mapping>
записи в web.xml
)
<p:graphicImage>
кстати, точно такая же проблема с StreamedContent
, Смотрите также среди прочего Отображение динамического изображения из базы данных с помощью p: graphicImage и StreamedContent.
@BalusC, для
p:fileDownload
, есть ли способ разгрузить создание StreamedContent другому объекту, который затем можно было бы вызывать непосредственно из JSF? Подобно тому, как вы разгружаетесь
p:graphicImage
здесь . Если да, то какова будет область применения этого специального объекта? Я предполагаю, что RequestScoped так как не будет никакой связи между initDownload и getDownload. ApplicationScoped не сможет отслеживать все загрузки в течение одного сеанса, верно? Мне также интересно, не слишком ли дорого создание нового объекта Apache FOP в каждом запросе?
Вот пример:
jsf:
<h:commandButton value="print/download" action="#{streamhelper.initDownload()}">
<p:fileDownload value="#{streamhelper.download}"/>
<f:param name="html" value="#{bean.html}" />
<f:param name="idNum" value="#{bean.idNum}" />
</h:commandButton>
специальный объект:
@Named("streamhelper") @RequestScoped @Getter @Setter @Slf4j
public class StreamedContentHelper
{
@PostConstruct @SneakyThrows({NamingException.class})
public void init(){
fop = util.getLocator().getObject(util.getLocator().prependPortableName(FOPEngineImpl.class.getSimpleName()));
}
public void initDownload() throws Exception
{
FacesContext context = FacesContext.getCurrentInstance();
log.trace("context PhaseID: {}", context.getCurrentPhaseId());
String html = context.getExternalContext().getRequestParameterMap().get("html");
String idNum = context.getExternalContext().getRequestParameterMap().get("idNum");
byte[] attachBytes = fop.getPDFBytes(html);
InputStream stream = new ByteArrayInputStream(attachBytes);
stream.mark(0); //remember to this position!
String filename = String.format("%s-download.pdf", loadNum);
download = new DefaultStreamedContent(stream, "application/pdf", filename);
}
private StreamedContent download;
private FOPEngineLocal fop;
private @Inject Util util;
}