Java tutorial
/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package com.xpn.xwiki.web; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.velocity.VelocityContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiAttachment; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.plugin.fileupload.FileUploadPlugin; /** * Action that handles uploading document attachments. It saves all the uploaded files whose fieldname start with * {@code filepath}. * * @version $Id: 3d77f5772fde20ba5424c2d85a98a669ea5e32dc $ */ public class UploadAction extends XWikiAction { /** Logging helper object. */ private static final Logger LOGGER = LoggerFactory.getLogger(UploadAction.class); /** The prefix of the accepted file input field name. */ private static final String FILE_FIELD_NAME = "filepath"; /** The prefix of the corresponding filename input field name. */ private static final String FILENAME_FIELD_NAME = "filename"; /** * {@inheritDoc} * * @see XWikiAction#action(XWikiContext) */ @Override public boolean action(XWikiContext context) throws XWikiException { XWikiResponse response = context.getResponse(); Object exception = context.get("exception"); boolean ajax = ((Boolean) context.get("ajax")).booleanValue(); // check Exception File upload is large if (exception != null) { if (exception instanceof XWikiException) { XWikiException exp = (XWikiException) exception; if (exp.getCode() == XWikiException.ERROR_XWIKI_APP_FILE_EXCEPTION_MAXSIZE) { response.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE); ((VelocityContext) context.get("vcontext")).put("message", "core.action.upload.failure.maxSize"); context.put("message", "fileuploadislarge"); return true; } } } // CSRF prevention if (!csrfTokenCheck(context)) { return false; } XWikiDocument doc = context.getDoc().clone(); // The document is saved for each attachment in the group. FileUploadPlugin fileupload = (FileUploadPlugin) context.get("fileuploadplugin"); Map<String, String> fileNames = new HashMap<String, String>(); List<String> wrongFileNames = new ArrayList<String>(); Map<String, String> failedFiles = new HashMap<String, String>(); for (String fieldName : fileupload.getFileItemNames(context)) { try { if (fieldName.startsWith(FILE_FIELD_NAME)) { String fileName = getFileName(fieldName, fileupload, context); if (fileName != null) { fileNames.put(fileName, fieldName); } } } catch (Exception ex) { wrongFileNames.add(fileupload.getFileName(fieldName, context)); } } for (Entry<String, String> file : fileNames.entrySet()) { try { uploadAttachment(file.getValue(), file.getKey(), fileupload, doc, context); } catch (Exception ex) { LOGGER.warn("Saving uploaded file failed", ex); failedFiles.put(file.getKey(), ExceptionUtils.getRootCauseMessage(ex)); } } LOGGER.debug("Found files to upload: " + fileNames); LOGGER.debug("Failed attachments: " + failedFiles); LOGGER.debug("Wrong attachment names: " + wrongFileNames); if (ajax) { try { response.getOutputStream().println("ok"); } catch (IOException ex) { LOGGER.error("Unhandled exception writing output:", ex); } return false; } // Forward to the attachment page if (failedFiles.size() > 0 || wrongFileNames.size() > 0) { ((VelocityContext) context.get("vcontext")).put("message", "core.action.upload.failure"); ((VelocityContext) context.get("vcontext")).put("failedFiles", failedFiles); ((VelocityContext) context.get("vcontext")).put("wrongFileNames", wrongFileNames); return true; } String redirect = fileupload.getFileItemAsString("xredirect", context); if (StringUtils.isEmpty(redirect)) { redirect = context.getDoc().getURL("attach", true, context); } sendRedirect(response, redirect); return false; } /** * Attach a file to the current document. * * @param fieldName the target file field * @param filename * @param fileupload the {@link FileUploadPlugin} holding the form data * @param doc the target document * @param context the current request context * @return {@code true} if the file was successfully attached, {@code false} otherwise. * @throws XWikiException if the form data cannot be accessed, or if the database operation failed */ public boolean uploadAttachment(String fieldName, String filename, FileUploadPlugin fileupload, XWikiDocument doc, XWikiContext context) throws XWikiException { XWikiResponse response = context.getResponse(); String username = context.getUser(); // Read XWikiAttachment XWikiAttachment attachment = doc.getAttachment(filename); if (attachment == null) { attachment = new XWikiAttachment(); doc.getAttachmentList().add(attachment); } try { attachment.setContent(fileupload.getFileItemInputStream(fieldName, context)); } catch (IOException e) { throw new XWikiException(XWikiException.MODULE_XWIKI_APP, XWikiException.ERROR_XWIKI_APP_UPLOAD_FILE_EXCEPTION, "Exception while reading uploaded parsed file", e); } attachment.setFilename(filename); attachment.setAuthor(username); // Add the attachment to the document attachment.setDoc(doc); doc.setAuthor(username); if (doc.isNew()) { doc.setCreator(username); } // Adding a comment with a link to the download URL String comment; String nextRev = attachment.getNextVersion(); ArrayList<String> params = new ArrayList<String>(); params.add(filename); params.add(doc.getAttachmentRevisionURL(filename, nextRev, context)); if (attachment.isImage(context)) { comment = context.getMessageTool().get("core.comment.uploadImageComment", params); } else { comment = context.getMessageTool().get("core.comment.uploadAttachmentComment", params); } // Save the document. try { context.getWiki().saveDocument(doc, comment, context); } catch (XWikiException e) { // check Exception is ERROR_XWIKI_APP_JAVA_HEAP_SPACE when saving Attachment if (e.getCode() == XWikiException.ERROR_XWIKI_APP_JAVA_HEAP_SPACE) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); context.put("message", "javaheapspace"); return true; } throw e; } return false; } /** * Extract the corresponding attachment name for a given file field. It can either be specified in a separate form * input field, or it is extracted from the original filename. * * @param fieldName the target file field * @param fileupload the {@link FileUploadPlugin} holding the form data * @param context the current request context * @return a valid attachment name * @throws XWikiException if the form data cannot be accessed, or if the specified filename is invalid */ protected String getFileName(String fieldName, FileUploadPlugin fileupload, XWikiContext context) throws XWikiException { String filenameField = FILENAME_FIELD_NAME + fieldName.substring(FILE_FIELD_NAME.length()); String filename = null; // Try to use the name provided by the user filename = fileupload.getFileItemAsString(filenameField, context); if (!StringUtils.isBlank(filename)) { // TODO These should be supported, the URL should just contain escapes. if (filename.indexOf("/") != -1 || filename.indexOf("\\") != -1 || filename.indexOf(";") != -1) { throw new XWikiException(XWikiException.MODULE_XWIKI_APP, XWikiException.ERROR_XWIKI_APP_INVALID_CHARS, "Invalid filename: " + filename); } } if (StringUtils.isBlank(filename)) { // Try to get the actual filename on the client String fname = fileupload.getFileName(fieldName, context); if (StringUtils.indexOf(fname, "/") >= 0) { fname = StringUtils.substringAfterLast(fname, "/"); } if (StringUtils.indexOf(fname, "\\") >= 0) { fname = StringUtils.substringAfterLast(fname, "\\"); } filename = fname; } // Sometimes spaces are replaced with '+' by the browser. filename = filename.replaceAll("\\+", " "); if (StringUtils.isBlank(filename)) { // The file field was left empty, ignore this return null; } // Issues fixed by the clearName : // 1) Attaching images with a name containing special characters generates bugs // (image are not displayed), XWIKI-2090. // 2) Attached files that we can't delete or link in the Wiki pages, XWIKI-2087. filename = context.getWiki().clearName(filename, false, true, context); return filename; } /** * {@inheritDoc} * * @see XWikiAction#render(XWikiContext) */ @Override public String render(XWikiContext context) throws XWikiException { boolean ajax = ((Boolean) context.get("ajax")).booleanValue(); if (ajax) { try { context.getResponse().getOutputStream() .println("error: " + context.getMessageTool().get((String) context.get("message"))); } catch (IOException ex) { LOGGER.error("Unhandled exception writing output:", ex); } return null; } ((VelocityContext) context.get("vcontext")).put("viewer", "uploadfailure"); return "view"; } }