source: other-projects/the-macronizer/trunk/src/java/web/servlets/FileUpload.java@ 35720

Last change on this file since 35720 was 35720, checked in by cstephen, 2 years ago

Refactor FileUpload logic

File size: 12.6 KB
Line 
1package web.servlets;
2
3import java.io.File;
4import java.io.IOException;
5import java.util.ArrayList;
6import java.util.List;
7
8import javax.servlet.RequestDispatcher;
9import javax.servlet.ServletConfig;
10import javax.servlet.ServletException;
11import javax.servlet.http.HttpServlet;
12import javax.servlet.http.HttpServletRequest;
13import javax.servlet.http.HttpServletResponse;
14
15import monogram.plugin.PluginConfiguration;
16import monogram.plugin.PluginManager;
17
18import org.apache.commons.fileupload.FileItem;
19import org.apache.commons.fileupload.FileUploadException;
20import org.apache.commons.fileupload.disk.DiskFileItemFactory;
21import org.apache.commons.fileupload.servlet.ServletFileUpload;
22import org.apache.log4j.*;
23
24import util.FileUtil;
25
26/**
27 * @author University of Waikato - Te Whare Wānanga o Waikato
28 * @version 1.0
29 * @since 2014-11-20
30 */
31public class FileUpload extends HttpServlet {
32
33 // Create an instance of the logger object defined for the DirectInput servlet in log4j.properties.
34 private static final Logger logger = Logger.getLogger(web.servlets.DirectInput.class.getName());
35
36 private File tmpdir;
37 private PluginManager pluginManager;
38
39 @Override
40 public void init(ServletConfig config)
41 throws ServletException
42 {
43 super.init(config);
44
45 tmpdir = new File((String) config.getServletContext().getAttribute("tmpdir"));
46 pluginManager = new PluginManager(tmpdir);
47 }
48
49 /**
50 * Handles the HTTP <code>GET</code> method.
51 * @param request The servlet request.
52 * @param response The servlet response.
53 * @throws IllegalStateException if a reponse has already been committed.
54 * @throws IOException if an I/O error occurs.
55 */
56 @Override
57 protected void doGet(HttpServletRequest request, HttpServletResponse response)
58 throws IllegalStateException, IOException
59 {
60 response.sendError(405); // 405 Method Not Allowed
61 }
62
63 /**
64 * Handles the HTTP <code>POST</code> method.
65 * @param request servlet request
66 * @param response servlet response
67 * @throws ServletException if a servlet-specific error occurs
68 * @throws IOException if an I/O error occurs
69 */
70 @Override
71 protected void doPost(HttpServletRequest request, HttpServletResponse response)
72 throws IllegalStateException, IOException, ServletException
73 {
74 List<FileItem> formItems = null;
75 Properties properties = null;
76 File restoredFile = null;
77 PluginConfiguration configuration = null;
78
79 try
80 {
81 formItems = parseFormItems(request);
82 properties = getRequestProperties(formItems);
83 }
84 catch (Exception ex)
85 {
86 throw new ServletException("Expected a multipart/form-data request.", ex);
87 }
88
89 try
90 {
91 Boolean foundFile = getRequestFile(formItems, properties);
92 if (!foundFile)
93 {
94 setError(properties, "FILE_NOT_FOUND_ERROR", request, response, true);
95 return;
96 }
97 }
98 catch (Exception ex)
99 {
100 ex.printStackTrace();
101 logger.error("Failed to get uploaded file", ex);
102 setError(properties, "UNEXPECTED_ERROR", request, response, false);
103 }
104
105 try
106 {
107 configuration = configure(properties);
108
109 if (configuration.getFile().length() == 0)
110 {
111 configuration.getFile().delete();
112 setError(properties, "FILE_NOT_FOUND_ERROR", request, response, false);
113 return;
114 }
115
116 restoredFile = pluginManager.run(configuration);
117 request.setAttribute("file", restoredFile);
118 request.setAttribute("fileType", configuration.getFileType());
119 request.setAttribute("charsetEncoding", "utf-8");
120 request.setAttribute("filename", properties.getFilename());
121 request.setAttribute("preserveMacrons", properties.getPreserveExistingMacrons());
122 request.setAttribute("options", properties.getShowAdvancedOptions());
123
124 forward(properties.getJspForwardPath() + "/main.jsp", request, response);
125 }
126 catch (UnsupportedOperationException uoex)
127 {
128 FileUtil.deleteFile(restoredFile);
129 logger.error("Failed to restore macrons on a file", uoex);
130 setError(properties, "FILE_TYPE_NOT_SUPPORTED_ERROR", request, response, false);
131 }
132 catch (Exception ex)
133 {
134 FileUtil.deleteFile(restoredFile);
135 ex.printStackTrace();
136 logger.error("Failed to restore macrons on a file", ex);
137 setError(properties, "UNEXPECTED_ERROR", request, response, false);
138 }
139 finally
140 {
141 if (configuration != null)
142 {
143 FileUtil.deleteFile(configuration.getFile());
144 // We don't clean up the file held by the properties object,
145 // as the configuration holds a reference to said file
146 }
147 }
148 }
149
150 private void setError(
151 Properties properties,
152 String errorCode,
153 HttpServletRequest request,
154 HttpServletResponse response,
155 Boolean isClientError)
156 throws IOException, ServletException
157 {
158 switch (properties.getOutputType()) {
159 case Json:
160 // 500 Internal Server Error
161 response.sendError(isClientError ? 400 : 500, errorCode);
162 break;
163 default:
164 request.setAttribute("errorMessage", errorCode);
165 forward(properties.getJspForwardPath()+ "/error.jsp", request, response);
166 break;
167 }
168 }
169
170 /**
171 * Forwards a request from a servlet to another resource on the server.
172 * @param path The path to forward to.
173 * @param request The servlet request.
174 * @param response The servlet response.
175 * @throws ServletException
176 * @throws IOException
177 */
178 private void forward(String path, HttpServletRequest request, HttpServletResponse response)
179 throws ServletException, IOException
180 {
181 final RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(path);
182 dispatcher.forward(request, response);
183 }
184
185 /**
186 * Parses multipart items from a request.
187 * @param request The request
188 * @return The parsed multipart items.
189 * @throws FileUploadException
190 */
191 private List<FileItem> parseFormItems(HttpServletRequest request)
192 throws FileUploadException
193 {
194 DiskFileItemFactory factory = new DiskFileItemFactory();
195 factory.setSizeThreshold(10 * 1024 * 1024);
196 ServletFileUpload upload = new ServletFileUpload(factory);
197
198 List<FileItem> items = new ArrayList<>();
199 for (Object element : upload.parseRequest(request)) {
200 items.add(FileItem.class.cast(element));
201 }
202
203 return items;
204 }
205
206 /**
207 * Gets the properties of the request by checking the submitted form items.
208 * @param formItems The multipart form items.
209 * @return The properties.
210 */
211 private Properties getRequestProperties(List<FileItem> formItems)
212 {
213 Properties properties = new Properties();
214
215 for (FileItem item : formItems)
216 {
217 if (!item.isFormField())
218 {
219 continue;
220 }
221
222 String fieldName = item.getFieldName();
223 String fieldValue = item.getString();
224
225 switch (fieldName)
226 {
227 case "charsetEncoding":
228 properties.setCharsetEncoding(fieldValue);
229 break;
230 case "fileType":
231 properties.setFileType(fieldValue);
232 break;
233 case "preserveExistingMacrons":
234 properties.setPreserveExistingMacrons(fieldValue);
235 break;
236 case "lang":
237 properties.setLanguage(fieldValue);
238 break;
239 case "options":
240 properties.setShowAdvancedOptions(fieldValue);
241 break;
242 case "o":
243 properties.setOutputType(fieldValue);
244 break;
245 }
246 }
247
248 return properties;
249 }
250
251 /**
252 * Gets the first file contained in the submitted form items.
253 * @param formItems The multipart form items.
254 * @return The file, or <code>null</code> if the request contained no files.
255 * @throws Exception
256 * @throws IOException
257 */
258 private Boolean getRequestFile(List<FileItem> formItems, Properties properties)
259 throws Exception, IOException
260 {
261 for (FileItem item : formItems)
262 {
263 if (item.isFormField())
264 {
265 continue;
266 }
267
268 String fileType = FileUtil.guessFileType(new File(item.getName()));
269 File file = File.createTempFile(FileUtil.TMP_FILE_PREFIX, fileType, tmpdir);
270 item.write(file);
271
272 properties.setFile(file);
273 properties.setFilename(item.getName());
274
275 return true;
276 }
277
278 return false;
279 }
280
281 /* Useful for debugging file operations */
282 // private String readStringFromFile(File file)
283 // throws FileNotFoundException, IOException
284 // {
285 // BufferedReader reader = new BufferedReader(new FileReader(file));
286 // String line = null;
287 // StringBuilder sb = new StringBuilder();
288
289 // try
290 // {
291 // while ((line=reader.readLine()) != null)
292 // {
293 // sb.append(line);
294 // }
295 // }
296 // finally
297 // {
298 // reader.close();
299 // }
300
301 // return sb.toString();
302 // }
303
304 private PluginConfiguration configure(Properties properties)
305 {
306 final File file = properties.getFile();
307 final String fileType = properties.getFileType().equals("(detect automatically)") ? FileUtil.guessFileType(file) : properties.getFileType();
308 final String charsetEncoding = properties.getCharsetEncoding().equals("(detect automatically)") ? "utf8" : properties.getCharsetEncoding();
309
310 final PluginConfiguration configuration = new PluginConfiguration();
311 configuration.setFile(file);
312 configuration.setFileType(fileType);
313 configuration.setCharsetEncoding(charsetEncoding);
314 configuration.setPreserveExistingMacrons(properties.getPreserveExistingMacrons());
315
316 return configuration;
317 }
318
319 private class Properties
320 {
321 private File file;
322 private String filename;
323 private String fileType;
324 private String charsetEncoding;
325 private Boolean preserveExistingMacrons;
326 private String language;
327 private Boolean showAdvancedOptions;
328 private OutputType outputType;
329
330 public File getFile() {
331 return file;
332 }
333
334 public String getFilename() {
335 return filename;
336 }
337
338 public String getFileType() {
339 return fileType;
340 }
341
342 public String getCharsetEncoding() {
343 return charsetEncoding;
344 }
345
346 public Boolean getPreserveExistingMacrons() {
347 return preserveExistingMacrons;
348 }
349
350 public String getLanguage() {
351 return language;
352 }
353
354 public Boolean getShowAdvancedOptions() {
355 return showAdvancedOptions;
356 }
357
358 public OutputType getOutputType() {
359 return outputType;
360 }
361
362 public String getJspForwardPath() {
363 return getLanguage() != null
364 ? "/jsp/" + getLanguage()
365 : "/jsp/mi";
366 }
367
368 public void setFile(File file) {
369 this.file = file;
370 }
371
372 public void setFilename(String filename) {
373 this.filename = filename;
374 }
375
376 public void setFileType(String fileType) {
377 this.fileType = fileType;
378 }
379
380 public void setCharsetEncoding(String charsetEncoding) {
381 this.charsetEncoding = charsetEncoding;
382 }
383
384 public void setPreserveExistingMacrons(String preserveExistingMacrons) {
385 this.preserveExistingMacrons = Boolean.parseBoolean(preserveExistingMacrons);
386 }
387
388 public void setLanguage(String language) {
389 this.language = language;
390 }
391
392 public void setShowAdvancedOptions(String options) {
393 this.showAdvancedOptions = Boolean.parseBoolean(options);
394 }
395
396 public void setOutputType(String outputType) {
397 this.outputType = OutputType.parse(outputType);
398 }
399 }
400}
Note: See TracBrowser for help on using the repository browser.