Как использовать потоковый контент с 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;
}
Другие вопросы по тегам