1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package com.xpn.xwiki.plugin.fileupload

File FileUploadPlugin.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart7.png
64% of files have more coverage

Code metrics

30
90
19
1
457
227
41
0.46
4.74
19
2.16

Classes

Class Line # Actions
FileUploadPlugin 54 90 0% 41 41
0.70503670.5%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20   
21    package com.xpn.xwiki.plugin.fileupload;
22   
23    import java.io.File;
24    import java.io.IOException;
25    import java.io.InputStream;
26    import java.util.ArrayList;
27    import java.util.List;
28   
29    import org.apache.commons.fileupload.FileItem;
30    import org.apache.commons.fileupload.FileUpload;
31    import org.apache.commons.fileupload.FileUploadBase;
32    import org.apache.commons.fileupload.RequestContext;
33    import org.apache.commons.fileupload.disk.DiskFileItem;
34    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
35    import org.apache.commons.fileupload.servlet.ServletFileUpload;
36    import org.apache.commons.fileupload.servlet.ServletRequestContext;
37    import org.apache.commons.io.FilenameUtils;
38    import org.slf4j.Logger;
39    import org.slf4j.LoggerFactory;
40   
41    import com.xpn.xwiki.XWiki;
42    import com.xpn.xwiki.XWikiContext;
43    import com.xpn.xwiki.XWikiException;
44    import com.xpn.xwiki.api.Api;
45    import com.xpn.xwiki.plugin.XWikiDefaultPlugin;
46    import com.xpn.xwiki.plugin.XWikiPluginInterface;
47   
48    /**
49    * Plugin that offers access to uploaded files. The uploaded files are automatically parsed and preserved as a list of
50    * {@link FileItem}s.
51    *
52    * @version $Id: a75f2176cdfcbd18e9d5703f162869156a680f15 $
53    */
 
54    public class FileUploadPlugin extends XWikiDefaultPlugin
55    {
56    /**
57    * The name of the plugin; the key that can be used to retrieve this plugin from the context.
58    *
59    * @see XWikiPluginInterface#getName()
60    */
61    public static final String PLUGIN_NAME = "fileupload";
62   
63    /**
64    * The context name of the uploaded file list. It can be used to retrieve the list of uploaded files from the
65    * context.
66    */
67    public static final String FILE_LIST_KEY = "fileuploadlist";
68   
69    /**
70    * The name of the parameter that can be set in the global XWiki preferences to override the default maximum file
71    * size.
72    */
73    public static final String UPLOAD_MAXSIZE_PARAMETER = "upload_maxsize";
74   
75    /**
76    * The name of the parameter that can be set in the global XWiki preferences to override the default size threshold
77    * for on-disk storage.
78    */
79    public static final String UPLOAD_SIZETHRESHOLD_PARAMETER = "upload_sizethreshold";
80   
81    /**
82    * Log object to log messages in this class.
83    */
84    private static final Logger LOGGER = LoggerFactory.getLogger(FileUploadPlugin.class);
85   
86    /**
87    * The default maximum size for uploaded documents. This limit can be changed using the <tt>upload_maxsize</tt>
88    * XWiki preference.
89    */
90    private static final long UPLOAD_DEFAULT_MAXSIZE = 33554432L;
91   
92    /**
93    * The default maximum size for in-memory stored uploaded documents. If a file is larger than this limit, it will be
94    * stored on disk until the current request finishes. This limit can be changed using the
95    * <tt>upload_sizethreshold</tt> XWiki preference.
96    */
97    private static final long UPLOAD_DEFAULT_SIZETHRESHOLD = 100000L;
98   
99    /**
100    * @param name the plugin name
101    * @param className the plugin classname (used in logs for example)
102    * @param context the XWiki Context
103    * @see XWikiDefaultPlugin#XWikiDefaultPlugin(String,String,com.xpn.xwiki.XWikiContext)
104    */
 
105  10 toggle public FileUploadPlugin(String name, String className, XWikiContext context)
106    {
107  10 super(name, className, context);
108    }
109   
 
110  0 toggle @Override
111    public String getName()
112    {
113  0 return PLUGIN_NAME;
114    }
115   
 
116  0 toggle @Override
117    public void init(XWikiContext context)
118    {
119  0 super.init(context);
120    }
121   
 
122  0 toggle @Override
123    public void virtualInit(XWikiContext context)
124    {
125  0 super.virtualInit(context);
126    }
127   
 
128  0 toggle @Override
129    public Api getPluginApi(XWikiPluginInterface plugin, XWikiContext context)
130    {
131  0 return new FileUploadPluginApi((FileUploadPlugin) plugin, context);
132    }
133   
134    /**
135    * {@inheritDoc}
136    * <p>
137    * Make sure we don't leave files in temp directories and in memory.
138    * </p>
139    */
 
140  0 toggle @Override
141    public void endRendering(XWikiContext context)
142    {
143    // we used to call cleanFileList here but we should not anymore as endRendering is called to
144    // many times and empties the file upload list. This is handled by XWikiAction and
145    // XWikiPortlet which clean up lists in a finally block
146    }
147   
148    /**
149    * Deletes all temporary files of the upload.
150    *
151    * @param context Context of the request.
152    * @see FileUploadPluginApi#cleanFileList()
153    */
 
154  10 toggle public void cleanFileList(XWikiContext context)
155    {
156  10 LOGGER.debug("Cleaning uploaded files");
157   
158  10 List<FileItem> fileuploadlist = getFileItems(context);
159  10 if (fileuploadlist != null) {
160  10 for (FileItem item : fileuploadlist) {
161  38 try {
162  38 item.delete();
163    } catch (Exception ex) {
164  0 LOGGER.warn("Exception cleaning uploaded files", ex);
165    }
166    }
167  10 context.remove(FILE_LIST_KEY);
168    }
169    }
170   
171    /**
172    * Loads the list of uploaded files in the context if there are any uploaded files.
173    *
174    * @param context Context of the request.
175    * @throws XWikiException An XWikiException is thrown if the request could not be parsed.
176    * @see FileUploadPluginApi#loadFileList()
177    */
 
178  10 toggle public void loadFileList(XWikiContext context) throws XWikiException
179    {
180  10 XWiki xwiki = context.getWiki();
181  10 loadFileList(
182    xwiki.getSpacePreferenceAsLong(UPLOAD_MAXSIZE_PARAMETER, UPLOAD_DEFAULT_MAXSIZE, context),
183    (int) xwiki.getSpacePreferenceAsLong(UPLOAD_SIZETHRESHOLD_PARAMETER, UPLOAD_DEFAULT_SIZETHRESHOLD, context),
184    xwiki.Param("xwiki.upload.tempdir"), context);
185    }
186   
187    /**
188    * Loads the list of uploaded files in the context if there are any uploaded files.
189    *
190    * @param uploadMaxSize Maximum size of the uploaded files.
191    * @param uploadSizeThreashold Threashold over which the file data should be stored on disk, and not in memory.
192    * @param tempdir Temporary directory to store the uploaded files that are not kept in memory.
193    * @param context Context of the request.
194    * @throws XWikiException if the request could not be parsed, or the maximum file size was reached.
195    * @see FileUploadPluginApi#loadFileList(long, int, String)
196    */
 
197  10 toggle public void loadFileList(long uploadMaxSize, int uploadSizeThreashold, String tempdir, XWikiContext context)
198    throws XWikiException
199    {
200  10 LOGGER.debug("Loading uploaded files");
201   
202    // If we already have a file list then loadFileList was already called
203    // Continuing would empty the list.. We need to stop.
204  10 if (context.get(FILE_LIST_KEY) != null) {
205  0 LOGGER.debug("Called loadFileList twice");
206   
207  0 return;
208    }
209   
210    // Get the FileUpload Data
211    // Make sure the factory only ever creates file items which will be deleted when the jvm is stopped.
212  10 DiskFileItemFactory factory = new DiskFileItemFactory()
213    {
 
214  38 toggle public FileItem createItem(String fieldName, String contentType, boolean isFormField, String fileName)
215    {
216  38 try {
217  38 final DiskFileItem item =
218    (DiskFileItem) super.createItem(fieldName, contentType, isFormField, fileName);
219    // Needed to make sure the File object is created.
220  38 item.getOutputStream();
221  38 item.getStoreLocation().deleteOnExit();
222  38 return item;
223    } catch (IOException e) {
224  0 String path = System.getProperty("java.io.tmpdir");
225  0 if (super.getRepository() != null) {
226  0 path = super.getRepository().getPath();
227    }
228  0 throw new RuntimeException("Unable to create a temporary file for saving the attachment. "
229    + "Do you have write access on " + path + "?");
230    }
231    }
232    };
233   
234  10 factory.setSizeThreshold(uploadSizeThreashold);
235   
236  10 if (tempdir != null) {
237  0 File tempdirFile = new File(tempdir);
238  0 if (tempdirFile.mkdirs() && tempdirFile.canWrite()) {
239  0 factory.setRepository(tempdirFile);
240    }
241    }
242   
243    // TODO: Does this work in portlet mode, or we must use PortletFileUpload?
244  10 FileUpload fileupload = new ServletFileUpload(factory);
245  10 RequestContext reqContext = new ServletRequestContext(context.getRequest().getHttpServletRequest());
246  10 fileupload.setSizeMax(uploadMaxSize);
247    // context.put("fileupload", fileupload);
248   
249  10 try {
250  10 @SuppressWarnings("unchecked")
251    List<FileItem> list = fileupload.parseRequest(reqContext);
252  10 if (list.size() > 0) {
253  10 LOGGER.info("Loaded " + list.size() + " uploaded files");
254    }
255    // We store the file list in the context
256  10 context.put(FILE_LIST_KEY, list);
257    } catch (FileUploadBase.SizeLimitExceededException e) {
258  0 throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
259    XWikiException.ERROR_XWIKI_APP_FILE_EXCEPTION_MAXSIZE, "Exception uploaded file");
260    } catch (Exception e) {
261  0 throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
262    XWikiException.ERROR_XWIKI_APP_UPLOAD_PARSE_EXCEPTION, "Exception while parsing uploaded file", e);
263    }
264    }
265   
266    /**
267    * Allows to retrieve the current list of uploaded files, as a list of {@link FileItem}s.
268    * {@link #loadFileList(XWikiContext)} needs to be called beforehand
269    *
270    * @param context Context of the request.
271    * @return A list of FileItem elements.
272    * @see FileUploadPluginApi#getFileItems()
273    */
 
274  80 toggle public List<FileItem> getFileItems(XWikiContext context)
275    {
276  80 return (List<FileItem>) context.get(FILE_LIST_KEY);
277    }
278   
279    /**
280    * Allows to retrieve the contents of an uploaded file as a sequence of bytes. {@link #loadFileList(XWikiContext)}
281    * needs to be called beforehand.
282    *
283    * @param formfieldName The name of the form field.
284    * @param context Context of the request.
285    * @return The contents of the file.
286    * @throws XWikiException if the data could not be read.
287    * @see FileUploadPluginApi#getFileItemData(String)
288    */
 
289  20 toggle public byte[] getFileItemData(String formfieldName, XWikiContext context) throws XWikiException
290    {
291  20 int size = getFileItemSize(formfieldName, context);
292   
293  20 if (size == 0) {
294  10 return null;
295    }
296   
297  10 byte[] data = new byte[size];
298   
299  10 try {
300  10 InputStream fileis = getFileItemInputStream(formfieldName, context);
301  10 if (fileis != null) {
302  10 fileis.read(data);
303  10 fileis.close();
304    }
305    } catch (java.lang.OutOfMemoryError e) {
306  0 throw new XWikiException(XWikiException.MODULE_XWIKI_APP, XWikiException.ERROR_XWIKI_APP_JAVA_HEAP_SPACE,
307    "Java Heap Space, Out of memory exception", e);
308    } catch (IOException ie) {
309  0 throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
310    XWikiException.ERROR_XWIKI_APP_UPLOAD_FILE_EXCEPTION, "Exception while reading uploaded parsed file",
311    ie);
312    }
313   
314  10 return data;
315    }
316   
317    /**
318    * Allows to retrieve the contents of an uploaded file as a stream. {@link #loadFileList(XWikiContext)} needs to be
319    * called beforehand.
320    *
321    * @param formfieldName The name of the form field.
322    * @param context Context of the request.
323    * @return a InputStream on the file content
324    * @throws IOException if I/O problem occurs
325    * @since 2.3M2
326    */
 
327  20 toggle public InputStream getFileItemInputStream(String formfieldName, XWikiContext context) throws IOException
328    {
329  20 FileItem fileitem = getFile(formfieldName, context);
330   
331  20 if (fileitem == null) {
332  0 return null;
333    }
334   
335  20 return fileitem.getInputStream();
336    }
337   
338    /**
339    * Retrieve the size of a file content in byte. {@link #loadFileList(XWikiContext)} needs to be called beforehand.
340    *
341    * @param formfieldName The name of the form field.
342    * @param context Context of the request.
343    * @return the size of the file in byte
344    * @since 2.3M2
345    */
 
346  20 toggle public int getFileItemSize(String formfieldName, XWikiContext context)
347    {
348  20 FileItem fileitem = getFile(formfieldName, context);
349   
350  20 if (fileitem == null) {
351  6 return 0;
352    }
353   
354  14 return ((int) fileitem.getSize());
355    }
356   
357    /**
358    * Allows to retrieve the contents of an uploaded file as a string. {@link #loadFileList(XWikiContext)} needs to be
359    * called beforehand.
360    *
361    * @param formfieldName The name of the form field.
362    * @param context Context of the request.
363    * @return The contents of the file.
364    * @throws XWikiException if the data could not be read.
365    * @see FileUploadPluginApi#getFileItemAsString(String)
366    */
 
367  20 toggle public String getFileItemAsString(String formfieldName, XWikiContext context) throws XWikiException
368    {
369  20 byte[] data = getFileItemData(formfieldName, context);
370  20 if (data == null) {
371  10 return null;
372    }
373   
374  10 return new String(data);
375    }
376   
377    /**
378    * Allows to retrieve the contents of an uploaded file as a string. {@link #loadFileList(XWikiContext)} needs to be
379    * called beforehand.
380    *
381    * @deprecated not well named, use {@link #getFileItemAsString(String, com.xpn.xwiki.XWikiContext)}
382    * @param formfieldName The name of the form field.
383    * @param context Context of the request.
384    * @return The contents of the file.
385    * @throws XWikiException Exception is thrown if the data could not be read.
386    * @see FileUploadPluginApi#getFileItemAsString(String)
387    */
 
388  0 toggle @Deprecated
389    public String getFileItem(String formfieldName, XWikiContext context) throws XWikiException
390    {
391  0 return getFileItemAsString(formfieldName, context);
392    }
393   
394    /**
395    * Retrieves the list of FileItem names. {@link #loadFileList(XWikiContext)} needs to be called beforehand.
396    *
397    * @param context Context of the request
398    * @return List of strings of the item names
399    */
 
400  10 toggle public List<String> getFileItemNames(XWikiContext context)
401    {
402  10 List<String> itemnames = new ArrayList<String>();
403  10 List<FileItem> fileuploadlist = getFileItems(context);
404  10 if (fileuploadlist == null) {
405  0 return itemnames;
406    }
407   
408  10 for (FileItem item : fileuploadlist) {
409  38 itemnames.add(item.getFieldName());
410    }
411   
412  10 return itemnames;
413    }
414   
415    /**
416    * Get the name of the file uploaded for a form field.
417    *
418    * @param formfieldName The name of the form field.
419    * @param context Context of the request.
420    * @return The file name, or <tt>null</tt> if no file was uploaded for that form field.
421    */
 
422  10 toggle public String getFileName(String formfieldName, XWikiContext context)
423    {
424  10 FileItem fileitem = getFile(formfieldName, context);
425   
426    // We need to strip the file path. See http://commons.apache.org/fileupload/faq.html#whole-path-from-IE
427  10 return (fileitem == null) ? null : FilenameUtils.getName(fileitem.getName());
428    }
429   
430    /**
431    * Return the FileItem corresponding to the file uploaded for a form field.
432    *
433    * @param formfieldName The name of the form field.
434    * @param context Context of the request.
435    * @return The corresponding FileItem, or <tt>null</tt> if no file was uploaded for that form field.
436    */
 
437  50 toggle public FileItem getFile(String formfieldName, XWikiContext context)
438    {
439  50 LOGGER.debug("Searching file uploaded for field " + formfieldName);
440   
441  50 List<FileItem> fileuploadlist = getFileItems(context);
442  50 if (fileuploadlist == null) {
443  0 return null;
444    }
445   
446  50 FileItem fileitem = null;
447  50 for (FileItem item : fileuploadlist) {
448  118 if (formfieldName.equals(item.getFieldName())) {
449  44 fileitem = item;
450  44 LOGGER.debug("Found uploaded file!");
451  44 break;
452    }
453    }
454   
455  50 return fileitem;
456    }
457    }