воскресенье, 2 сентября 2012 г.

Java EE: загрузка файла с помощью Servlet'a


Загрузка файлов на сервер - неотъемлемая часть любого web-приложения. И чтобы клиент не начудил, и не загрузил на сервер что ни зря, нужно очень строго контролировать процесс передачи данных. Эта статья нацелена на "не очень детальное" рассмотрение примера, в котором с помощью web-формы, мы будем загружать изображение на сервер (Tomcat 7), используя Servlet.


Начнем с того, почему я сказал, что это будет "не очень детальное" рассмотрение. Все по той причине, что в конце вы сможете скачать архив с уже готовым кодом, запустить проект и посмотреть на то, как все устроено. Более того, с java-документацией все пока в порядке и узнать, что какой метод делает, не составит труда. И так, концентрация будет именно на коде, который должен работать с загрузкой файлов.

В данном примере я буду использовать:
  • Eclipse Java EE IDE for Web Developers (Juno Release)
  • Tomcat 7
  • Apache Commons
  • HTML, CSS (это не столь важно)
  • Windows 7

Создание проекта

Начинаем с того, что создаем в Eclipse новый проект (Dynamic Web Project). Затем вкидываем в папку %WORK_SPACE%\%PROJECT_NAME%\WebContent\WEB-INF\lib\ библиотеки Apache Commons (commons-fileupload-1.2.2 и commons-io-2.4 (версии могут отличаться)). Эти библиотеки мы будем использовать в сервлете, для написания кода, который будет отвечать за загрузку файлов.

Пишем форму

Далее в папке %WORK_SPACE%\%PROJECT_NAME%\WebContent\ создаем index.html (или .jsp), с user-интерфейсом для загрузки файлов. Как вы уже догадались, нам тут понадобится html & css. Привожу далее код (исключительно код формы), который получился у меня:

1:  <form name="uploadFileForm" method="POST" action="/ImageUploader/UploadHandler" enctype="multipart/form-data">  
2:            <table>  
3:            <tr><td><input type="file" name="file" size="30" /></td></tr>  
4:            <tr><td><input name="uploadButton" type="submit" value="Upload file"/></td></tr>  
5:            </table>  
6:  </form>  

Для формы необходимо:
  1. Указать method="POST"
  2. Указать в action тот url, на который вы замапите сервлет, который будет обрабатывать форму (об этом далее)
  3. Указать enctype="multipart/form-data"

Пишем сервлет

Я долго думал, как оформить данный отрывок статьи и сошелся на том, что выложу код со скупым описанием того, что в нем происходит. Думаю это будет максимально полезным и безвредным для читателей.

1:  package com.imageuploader.controller;  
2:  import java.io.File;  
3:  import java.io.IOException;  
4:  import java.util.Iterator;  
5:  import java.util.List;  
6:  import javax.servlet.RequestDispatcher;  
7:  import javax.servlet.ServletException;  
8:  import javax.servlet.http.HttpServlet;  
9:  import javax.servlet.http.HttpServletRequest;  
10:  import javax.servlet.http.HttpServletResponse;  
11:  import org.apache.commons.fileupload.FileItem;  
12:  import org.apache.commons.fileupload.FileUploadException;  
13:  import org.apache.commons.fileupload.disk.DiskFileItemFactory;  
14:  import org.apache.commons.fileupload.servlet.ServletFileUpload;  
15:  public class UploadController extends HttpServlet {  
16:       private static final String STORAGE_DIR = "server_image_storage";  
17:       private static final int MAX_TMP_MEMORY_SIZE = 2*1024*1024;  
18:       private static final long MAX_SIZE = 2*1024*1024;  
19:       public void doPost(HttpServletRequest request, HttpServletResponse response)  
20:       throws IOException, ServletException {  
21:            String successMsg = "File successfully uploaded.";  
22:            String errorMsg = null;  
23:            boolean isMultipart = ServletFileUpload.isMultipartContent(request);  
24:            if (isMultipart) {  
25:                 DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();  
26:                 fileItemFactory.setSizeThreshold(MAX_TMP_MEMORY_SIZE);  
27:                 fileItemFactory.setRepository(new File(System.getProperty("java.io.tmpdir")));  
28:                 String uploadStorage = getServletContext().getRealPath("") + File.separator + STORAGE_DIR;  
29:                 File storageDir = new File(uploadStorage);  
30:                 if (!storageDir.isDirectory())  
31:                      storageDir.mkdir();  
32:                 ServletFileUpload upload = new ServletFileUpload(fileItemFactory);  
33:                 upload.setSizeMax(MAX_SIZE);  
34:                 try {  
35:                      List items = upload.parseRequest(request);  
36:                      Iterator iterator = items.iterator();  
37:                      while (iterator.hasNext()) {  
38:                           FileItem fileItem = (FileItem) iterator.next();  
39:                           if ( !fileItem.isFormField() ) {  
40:                                if ( fileItem.getSize() > 0 ) {  
41:                                     String fileName = new File(fileItem.getName()).getName();  
42:                                     int pintPosition = fileName.lastIndexOf(".");  
43:                                     String mimeType = fileName.substring(pintPosition, fileName.length());  
44:                                     if (mimeType.equals(".jpeg") ||   
45:                                          mimeType.equals(".gif") ||  
46:                                          mimeType.equals(".png") ||  
47:                                          mimeType.equals(".jpg")) {  
48:                                          String filePath = uploadStorage + File.separator + fileName;  
49:                                          File uploadedFile = new File(filePath);  
50:                                          fileItem.write(uploadedFile);  
51:                                          request.setAttribute("successMessage", successMsg);  
52:                                          request.setAttribute("imgDir", STORAGE_DIR);  
53:                                          request.setAttribute("imgName", fileName);  
54:                                     } else {  
55:                                          errorMsg = "File must have correct extension.";  
56:                                          break;  
57:                                     }                           
58:                                } else {  
59:                                     errorMsg = "Select some file for the uplading.";  
60:                                     break;  
61:                                }                                
62:                           }   
63:                      }  
64:                 } catch (FileUploadException e) {  
65:                      errorMsg = "Choose file with acceptable size.";  
66:                 } catch (Exception e) {  
67:                      errorMsg = "Try to figure out what a shit happened in the code.";  
68:                 }  
69:                 request.setAttribute("errorMessage", errorMsg);  
70:                 RequestDispatcher rd = request.getRequestDispatcher("/index.jsp");  
71:                 rd.forward(request, response);  
72:            }                 
73:       }  
74:  }  

В строке 28 я создаю путь к будущему хранилищу файлов, которые будут загружаться. В строке 30 идет проверка на существование этой директории, если ее нет, то она создается. В блоке try, мы разбираем request, идем по его содержимому, изымаем только те компоненты, которые нам нужны (файлы), смотрим, что эти файлы соответствуют нашим требованиям (размер, расширение: мы разрешаем загружать только изображения), записываем файл по указанному пути, сохраняем путь и передаем все в jsp-страницу.

Большинство не прокомментированных строк, имеют ссылку на документацию и легко можно разобрать, что происходит в той или иной строке.

В заключение

Теперь, что касается обещания о выложенном проекте. Его можно скачать с моего DropBox'a. Затем просто импортируйте его к себе в Eclipse и можете посмотреть на него в действии.
Все файлы, которые загружены с помощью данного проекта, можно найти по адресу: 

%WORK_SPACE%\.metadata\.plugins\org.eclipse.wst.server.core\tmp<ANY_NUMBER>\wtpwebapps\%PROJECT_NAME%\server_image_storage

Без детализации это все. Огромная благодарность в подготовке этого туториала Тарасу Романюку.

Комментариев нет:

Отправить комментарий