I form html tradizionali negli anni hanno dimostrato numerosi limiti. Quelli più notevoli erano impossibilità di effettuare validazione server side, impossibilità di gestire dei campi autopopolati durante la digitazione, post completo del form senza possibilità di parzializzazione.

Tutte queste cose hanno reso il web uno strumento difficilmente utilizzabile per applicazioni che richiedessero un’elevata interazione con l’utente.
Negli ultimi anni AJAX ha rivoluzionato i tradizionali form html permettendo ai programmatori di poter dialogare agilmente con il loro backend e permettendo agli utenti di godere una più dinamica esperienza web.
Recentemente ho avuto la necessità di gestire un campo file in un form ajax. Per form ajax intendo un form che invia i suoi dati serializzati al server e non fa il post dell’intera pagina.
La tecnologia utilizzata era jQuery così ho cercato un plugin che si integrasse bene con tale tecnologia e ho trovato AJAX Upload.
Inseriamo innanzitutto gli script javascript nell’header necessari alla definizione di jQuery e del plugin AJAX Upload:
...
<head>
...
<script type="text/javascript" src="js/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="js/ajaxupload.js"></script>
...
</head>
...
A questo punto bisogna creare il div che cliccato avvierà la procedura di caricamento file:
<div id="upload_button">Upload</div>
Infine dobbiamo inizializzare i campi di tipo file all’interno del nostro form:
$(document).ready(function() {
new AjaxUpload('upload_button_id', {action: 'uploadServletPath'});
}
Al rendering della pagina cliccando sul div upload_button si aprirà la finestra di dialogo per la scelta del file e il file sarà direttamente inviato via POST (senza però il POST dell’intera pagina) alla servlet di upload che abbiamo specificato nell’inizializzazione del componente.
Nel costruttore di inizializzazione possiamo anche passare altri parametri di configurazione e gestire alcuni eventi precedenti (o successivi) al caricamento utili per valorizzare altre variabili del form o per visualizzare la progressione del caricamento o il nome del file caricato sul server.
E’ anche possibile stabilire il tipo di risposta ricevuta dal server: html, xml o json.
Per chi fosse interessato ecco il codice Java necessario al salvataggio del file:
/*
* author: Roberto Rossi
*/
package it.bits4beats.servlets;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.*;
import java.util.Iterator;
import java.util.List;
public class UploadServlet extends HttpServlet {
private static Log log = LogFactory.getLog(UploadServlet.class);
public static final String DEFAULT_UPLOAD_DIR_INIT_PARAM = "DefaultUploadDir";
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* This is a default constructor for UploadServlet class.
*
*/
public UploadServlet(){
}
public void init() throws ServletException {
super.init();
}
/**
* Receive a file to be uploaded and save it into filesystem
*/
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException,java.io.IOException {
log.info("method not supported!");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException,java.io.IOException {
String defaultUploadDir = getServletConfig().getInitParameter(DEFAULT_UPLOAD_DIR_INIT_PARAM);
if (defaultUploadDir != null && !defaultUploadDir.equals("")) {
log.debug("> default upload dir: " + defaultUploadDir);
}
/* the uploading user */
String username = request.getParameter("username");
/* max size of file upload */
String sizeParam = request.getParameter("sz");
Long maxBytes = null;
if (sizeParam != null && !sizeParam.isEmpty()) {
try {
/* max 5Mb file uploaded */
maxBytes = new Long(5242880);
}
catch (NumberFormatException nfe) {
/* do nothing */
}
}
log.debug("remote user: " + request.getRemoteUser());
log.debug("user principal: " + (request.getUserPrincipal() == null ? "" : request.getUserPrincipal().getName()) );
log.debug("username: " + username);
String status = "true";
String message = "";
String savedfilename = "";
String attachmentname = "";
String originalFilename = "";
/* reading file streams */
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (isMultipart) {
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List items = null;
try {
items = upload.parseRequest(request);
if (items != null && items.size() > 0) {
File uploadDirFile = null;
String uploadDirFileRelativePath = null;
uploadDirFile = new File(getServletContext().getRealPath(defaultUploadDir));
uploadDirFileRelativePath = defaultUploadDir;
Iterator itr = items.iterator();
FileItem fileItem = null;
FileItem filenameItem = null;
while(itr.hasNext()) {
FileItem item = (FileItem) itr.next();
if (!item.isFormField()) {
fileItem = item;
}
else {
if (item.getFieldName().equals("attachmentFilename")) {
filenameItem = item;
log.debug("filenameItem name: " + filenameItem.getFieldName() + " valore: " + filenameItem.getString());
}
}
}
if (fileItem != null) {
/* there is a file item in the request */
String fileName = fileItem.getName();
log.debug("fileItem name: " + fileName);
if (fileItem.getName().contains("\\")) {
log.debug("Windows file");
/* windows file path */
fileName = fileItem.getName().substring(fileItem.getName().lastIndexOf("\\") +1);
log.debug("fileItem name: " + fileName);
}
File fullFile = new File(fileName);
File savedFile = new File(uploadDirFile, fileName);
if (savedFile != null) {
/* check file size */
if (maxBytes != null) {
if (fileItem.getSize() > maxBytes.longValue()) {
FileTooBigException ftbe = new FileTooBigException();
ftbe.setMaxSize(new Long(maxBytes.longValue()/1024/1024).toString());
throw ftbe;
}
}
if (savedFile.exists()) {
/* we need to create a file variant, save it and send it to the user */
String fileNameWithoutExtension = savedFile.getName().substring(0, savedFile.getName().lastIndexOf("."));
originalFilename = fileNameWithoutExtension;
String suffix = savedFile.getName().substring(savedFile.getName().lastIndexOf(".") + 1);
File newFile = File.createTempFile(fileNameWithoutExtension + "_", "." + suffix, uploadDirFile);
fileItem.write(newFile);
log.debug("newFile: " + newFile.getAbsolutePath());
status = "true";
message = "OK " + newFile.length() + " byte salvati";
/* relative file path */
savedfilename = uploadDirFileRelativePath + newFile.getName();
String filenameWithExt = extractFilenameWithExtensionFromPath(savedfilename);
attachmentname = extractFilename(filenameWithExt);
}
else {
log.debug("writing file to: " + savedFile.getAbsolutePath());
originalFilename = extractFilename(extractFilenameWithExtensionFromPath(savedFile.getAbsolutePath()));
fileItem.write(savedFile);
status = "true";
message = "OK " + savedFile.length() + " byte salvati";
/* relative file path */
savedfilename = uploadDirFileRelativePath + fullFile.getName();
String filenameWithExt = extractFilenameWithExtensionFromPath(savedfilename);
attachmentname = extractFilename(filenameWithExt);
}
}
else {
log.error("cannot create file");
status = "false";
message = "Errore generico di salvataggio";
}
}
}
else {
log.debug("no item to be saved");
}
}
catch (FileTooBigException ftbe) {
status = "false";
message = "File troppo grande, dimensione massima (Mb): " + ftbe.getMaxSize();
}
catch (Exception e1) {
e1.printStackTrace();
log.error("error in uploading");
status = "false";
message = "Errore generico di salvataggio";
}
}
PrintWriter out = response.getWriter();
String responseMessage = "{\"success\":" + status + ",\"message\":\"" + message + "\",\"savedfilename\":\"" + savedfilename + "\",\"attachmentname\":\"" + attachmentname + "\",\"originalfilename\":\"" + originalFilename + "\"}";
log.debug("responseMessage: " + responseMessage);
out.println(responseMessage);
}
/**
* This method extract filename (with extension) from filepath
*
* @param filepath
* @return filename without extension
*/
public static String extractFilenameWithExtensionFromPath(String filepath) {
if (filepath != null) {
int index = filepath.lastIndexOf("/");
if (index > 0 && index < (filepath.length() -1)) {
return filepath.substring(index + 1);
}
else return null;
}
else {
return null;
}
}
/**
* This method extract filename (without extension) from filename with extension
*
* @param filepath
* @return filename without extension
*/
public static String extractFilename(String filenameWithExtension) {
if (filenameWithExtension != null) {
int index = filenameWithExtension.lastIndexOf(".");
if (index > 0) {
return filenameWithExtension.substring(0, index);
}
else return filenameWithExtension;
}
else {
return null;
}
}
class FileTooBigException extends Exception {
private static final long serialVersionUID = 1L;
private String maxSize = null;
public String getMaxSize() {
return maxSize;
}
public void setMaxSize(String maxSize) {
this.maxSize = maxSize;
}
public FileTooBigException() {
super();
}
public FileTooBigException(String message) {
super(message);
}
}
}
Nel sito del plugin trovate anche una demo completa e funzionante di AJAX upload.