package com.im.imjutil.file;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import com.im.imjutil.report.csv.CSVElement;
import com.im.imjutil.report.csv.CSVMaker;
import com.im.imjutil.util.Pair;
/**
* Classe para fazer upload de arquivos via http.
* Esta depende do pacote:
* <a href="http://commons.apache.org/fileupload">Jakarta Commons FileUpload</a>.
* <br>
* A classe possui funcoes para leitura de arquivos sequenciais ou csv
* para arquivos texto e recuperacao de stream em arquivos binarios.
* <br>
* <b>
* OBSERVACAO: so e possivel executar uma unica leitura em um mesmo request.
* </b>
*
* @author Felipe Zappala
*/
public class HTTPFileUpload {
/**
* Proibindo a instanciacao
*/
private HTTPFileUpload() {
super();
}
/**
* Determina o tipo de leiruta feita no arquivo
*/
private enum ReadType {CSV, TEXT, BINARY, FIELD}
/**
* Faz a leitura dos diversos tipos de arquivos que possam
* estar contidos no request.
*
* @param <T> O tipo do objeto na lista de retornos.
* @param request O objeto de request do HTTP
* @param type O tipo de leitura a ser realizada.
* @return Uma lista do tipo de objeto contido no request
* @throws Exception caso ocorra algum erro de leitura
*/
@SuppressWarnings("unchecked")
private static <T> List<T> read(HttpServletRequest request, ReadType type)
throws Exception {
if (!ServletFileUpload.isMultipartContent(request)) {
throw new FileUploadException(
"Request does not contain a multipart/form-data");
}
List<T> rows = new ArrayList<T>();
ServletFileUpload upload = new ServletFileUpload();
FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext()) {
FileItemStream item = iter.next();
if (!item.isFormField()) {
InputStream stream = item.openStream();
switch (type) {
case TEXT:
rows.addAll((List<T>) readTextRows(stream));
break;
case CSV:
rows.addAll((List<T>) readCSVRows(stream));
break;
case BINARY:
rows.add((T)readBinaryFile(stream, item.getName()));
break;
default:
break;
}
}
}
return rows;
}
/**
* Faz a leitura dos campos de formulario que sao enviados
* junto com o arquivo no upload.
*
* @param request O objeto de request do HTTP
* @return
* @throws Exception
*/
public static List<Pair<String, String>> readFields(
HttpServletRequest request) throws Exception {
List<Pair<String, Object>> fields = readFormFields(request);
List<Pair<String, String>> list
= new ArrayList<Pair<String,String>>(fields.size());
for (Pair<String, Object> pair : fields) {
if (pair.getLast() instanceof String) {
list.add(Pair.make(pair.getFirst(), (String)pair.getLast()));
}
}
return list;
}
/**
* Faz a leitura dos arquivos contidos no formulario de upload.
*
* @param request O objeto de request do HTTP
* @return Uma lista de {@link HTTPFile} que representa um arquivo.
* @throws Exception caso ocorra algum erro.
*/
public static List<HTTPFile> readFiles(HttpServletRequest request)
throws Exception {
List<Pair<String, Object>> list = readFormFields(request);
List<HTTPFile> files = new ArrayList<HTTPFile>(list.size());
for (Pair<String, Object> pair : list) {
if (pair.getLast() instanceof HTTPFile) {
files.add((HTTPFile) pair.getLast());
}
}
return files;
}
@SuppressWarnings("unchecked")
private static <T> List<Pair<String, T>> readFormFields(
HttpServletRequest request) throws Exception {
if (!ServletFileUpload.isMultipartContent(request)) {
throw new FileUploadException(
"Request does not contain a multipart/form-data");
}
String charset = request.getCharacterEncoding();
if (charset == null) {
charset = "UTF-8";
}
List<Pair<String, T>> list = new ArrayList<Pair<String,T>>();
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = upload.parseRequest(request);
Pair<String, T> field;
for (FileItem item : items) {
field = new Pair<String, T>();
field.setFirst(item.getFieldName());
if (item.isFormField()) {
field.setLast((T) item.getString(charset));
} else {
field.setLast((T) convertToFileUploaded(item));
}
list.add(field);
}
return list;
}
/**
* Converte um FileItem em FileUploaded
*/
private static HTTPFile convertToFileUploaded(FileItem item) {
HTTPFile uploaded = new HTTPFile();
uploaded.setField(item.getFieldName());
uploaded.setName(item.getName());
uploaded.setSize(item.getSize());
uploaded.setBytes(item.get());
return uploaded;
}
public static Map<String, String> readFieldsMap(HttpServletRequest request)
throws Exception {
List<Pair<String, String>> list = readFields(request);
Map<String, String> map = new HashMap<String, String>(list.size());
for (Pair<String, String> pair : list) {
map.put(pair.getFirst(), pair.getLast());
}
return map;
}
/**
* Faz a leitura dos arquivos continos no upload do formulario para
* uma instancia de {@link HTTPFile} que contem todas as informacoes
* do arquivo, tais como nome, tamanho e o stream dos bytes independente
* do request.
*
* @param request
* @return
* @throws Exception
*/
public static Map<String, HTTPFile> readFilesdMap(
HttpServletRequest request) throws Exception {
List<HTTPFile> list = readFiles(request);
Map<String, HTTPFile> map
= new HashMap<String, HTTPFile>(list.size());
for (HTTPFile file: list) {
map.put(file.getField(), file);
}
return map;
}
/**
* Faz a leitura sequencial do arquivo, linha por linha, onde linhas
* em branco nao serao lidas, assim como os espacos apos o final da linha.
* <br>
* A leitura e feita somente em {@code request} com form do tipo multipart.
*
* @param request O request HTTP para obter o arquivo do formulario.
* @return Uma lista de strings que representa o arquivo.
* @throws IOException caso ocorra erros ao obter o arquivo.
* @throws FileUploadException caso o conteudo do request nao for multipart
*/
public static List<String> readText(HttpServletRequest request)
throws Exception {
return read(request, ReadType.TEXT);
}
/**
* Faz a leitura CSV do arquivo, separado por virgulas.
* <br>
* A leitura e feita somente em {@code request} com form do tipo multipart.
*
* @param request O request HTTP para obter o arquivo do formulario.
* @return Uma lista de strings que representa o arquivo.
* @throws IOException caso ocorra erros ao obter o arquivo.
* @throws FileUploadException caso o conteudo do request nao for multipart
*/
public static List<CSVElement> readCSV(HttpServletRequest request)
throws Exception {
return read(request, ReadType.CSV);
}
/**
* Faz a leitura dos arquivos binarios contidos na requisicao.
* <br>
* A leitura e feita somente em {@code request} com form do tipo multipart
* e so pode ter o stream acessado enquanto existir o request.
*
* @param request O request HTTP para obter o arquivo do formulario.
* @return Uma lista de pares que representa o nome do arquivo e seu stream.
* @throws IOException caso ocorra erros ao obter o arquivo.
* @throws FileUploadException caso o conteudo do request nao for multipart
*/
public static List<Pair<String, InputStream>> readBinary(
HttpServletRequest request) throws Exception {
return read(request, ReadType.BINARY);
}
/**
* Faz a leitura dos arquivos binarios contidos na requisicao.
* <br>
* A leitura e feita somente em {@code request} com form do tipo multipart
* e so pode ter o stream acessado enquanto existir o request.
*
* @param request O request HTTP para obter o arquivo do formulario.
* @return Um mapa com o nome do arquivo como chave e seu stream como valor.
* @throws IOException caso ocorra erros ao obter o arquivo.
* @throws FileUploadException caso o conteudo do request nao for multipart
*/
public static Map<String, InputStream> readBinaryMap(
HttpServletRequest request) throws Exception {
List<Pair<String, InputStream>> list = read(request, ReadType.BINARY);
Map<String, InputStream> map
= new HashMap<String, InputStream>(list.size());
for (Pair<String, InputStream> pair : list) {
map.put(pair.getFirst(), pair.getLast());
}
return map;
}
/**
* Faz a leitura do stream como um CSV padrao
*/
private static List<CSVElement> readCSVRows(InputStream stream)
throws Exception {
return CSVMaker.readCSV(stream);
}
/**
* Faz a leitura do arquivo como um par nome e stream
*/
private static Pair<String, InputStream> readBinaryFile(
InputStream stream, String fileName) {
Pair<String, InputStream> file = new Pair<String,InputStream>();
file.setFirst(fileName);
file.setLast(stream);
return file;
}
/**
* Faz a leitura das linhas do stream passado.
*/
private static List<String> readTextRows(InputStream stream)
throws IOException {
List<String> rows = new ArrayList<String>();
if(stream != null) {
BufferedReader reader = new BufferedReader(
new InputStreamReader(stream));
String row = "";
while ((row = reader.readLine()) != null) {
row = row.trim();
if (row.length() != 0) {
rows.add(row);
}
}
}
return rows;
}
public static Map<String, HTTPFile> readForm(HttpServletRequest request)
throws Exception {
List<Pair<String, Object>> fields = readFormFields(request);
Map<String, HTTPFile> map = new HashMap<String, HTTPFile>(fields.size());
for (Pair<String, Object> pair : fields) {
if (pair.getLast() instanceof HTTPFile) {
map.put(pair.getFirst(), (HTTPFile) pair.getLast());
} else if (pair.getLast() instanceof String) {
HTTPFile httpField = new HTTPFile();
httpField.setName(pair.getFirst());
httpField.setField((String)pair.getLast());
map.put(pair.getFirst(), httpField);
}
}
return map;
}
}