Deu trabalho, mas finalmente consegui deixar o upload e o download com o JSF totalmente funcional, sem usar outro framework como o PrimeFaces.
Eu desenvolvi para que o download e o upload vem e vão para um banco de dados.
upload.xhtml
<h:form enctype="multipart/form-data">
<h:outputLabel value="Arquivo: " for="arquivo"/>
<h:inputFile value="#{arquivoBean.uploadPart}" id="uploadPart"/>
<h:commandButton value="Upload" action="#{arquivoBean.uploadArquivo()}"/>
</h:form>
download.xhtml
<h:commandLink value="Baixar Arquivo" actionListener="#{arquivoBean.downloadArquivo(arquivo.id)}"/><br />
ArquivoBean.java, foi usado a lib commons-io-2.4.jar
@ManagedBean
public class ArquivoBean {
private Manager setManager = new Manager();
private UploadRepository repository = new UploadRepository();
private Arquivo arquivo = new Arquivo();
private Part arquivoPart;
public void uploadArquivo() {
try {
this.arquivo.setArquivo(IOUtils.toByteArray(arquivoPart.getInputStream()));
this.arquivo.setArquivoNome(pegarNomeDoArquivo(arquivoPart));
this.arquivo.setArquivoTamanho(arquivoPart.getSize());
} catch (IOException ex) {
Logger.getLogger(ArquivosBean.class.getName()).log(Level.SEVERE, null, ex);
}
repository.adiciona(this.arquivo);
this.arquivo = new Arquivo();
}
public void downloadArquivo(Long id) throws IOException {
FacesContext facesContext = FacesContext.getCurrentInstance();
HttpServletResponse response = this.setManager.response(facesContext);
Arquivo arquivo = repository.baixarArquivo(id);
response.reset();
response.setContentType("application/octet-stream");
response.setContentLength(arquivo.getArquivo().length);
response.setHeader("Content-disposition", "attachment; filename=" + arquivo.getArquivoNome());
OutputStream output = response.getOutputStream();
output.write(arquivo.getArquivo());
output.close();
facesContext.responseComplete();
}
private static String pegarNomeDoArquivo(Part part) {
for (String header : part.getHeader("content-disposition").split(";")) {
if (header.trim().startsWith("filename")) {
String nomeDoArquivo = header.substring(header.indexOf('=') + 1).trim().replace("\"", "");
return nomeDoArquivo.substring(nomeDoArquivo.lastIndexOf('/') + 1).substring(nomeDoArquivo.lastIndexOf('\\') + 1); // gambi para MSIE, não sei o porque mas já estava assim.
}
}
return null;
}
//GETS E SETS
}
ArquivoRepository.java
public class ArquivoRepository {
private Manager setManager = new Manager();
private EntityManager manager = this.setManager.getEntityManager();
public void adiciona(Arquivo Arquivo) {
this.manager.persist(Arquivo);
}
public Arquivo baixarArquivo(Long id) {
Query query = this.manager.createQuery("select o.arquivo, o.arquivoNome from Arquivo o WHERE o.id = ?");
query.setParameter(1, id);
List results = query.getResultList();
Iterator it = results.iterator();
Arquivo arquivo = new Arquivo();
if (it.hasNext()) {
Object[] result = (Object[]) it.next();
byte[] arquivo = (byte[]) result[0];
String nome = (String) result[1];
arquivo.setArquivo(arquivo);
arquivo.setArquivoNome(nome);
}
return arquivo;
}
//GETS E SETS
}
Manager.java
public class Manager {
public EntityManager getEntityManager() {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
HttpServletRequest request = (HttpServletRequest) ec.getRequest();
EntityManager manager = (EntityManager) request.getAttribute("EntityManager");
return manager;
}
public HttpServletResponse response(FacesContext fc) {
ExternalContext ec = fc.getExternalContext();
return (HttpServletResponse) ec.getResponse();
}
}
edit: criei uma classe apenas para chamar o EntityManager quando precisa-se e o response, para diminuir o numero de linhas.