Java tutorial
/* * CKFinder * ======== * http://cksource.com/ckfinder * Copyright (C) 2007-2015, CKSource - Frederico Knabben. All rights reserved. * * The software, this file and its contents are subject to the CKFinder * License. Please read the license.txt file before using, installing, copying, * modifying or distribute this file or part of its contents. The contents of * this file is part of the Source Code of CKFinder. */ package com.github.zhanhb.ckfinder.connector.handlers.command; import com.github.zhanhb.ckfinder.connector.api.AccessControl; import com.github.zhanhb.ckfinder.connector.api.CKFinderContext; import com.github.zhanhb.ckfinder.connector.api.ConnectorException; import com.github.zhanhb.ckfinder.connector.api.ErrorCode; import com.github.zhanhb.ckfinder.connector.api.FileUploadEvent; import com.github.zhanhb.ckfinder.connector.api.ResourceType; import com.github.zhanhb.ckfinder.connector.handlers.parameter.FileUploadParameter; import com.github.zhanhb.ckfinder.connector.utils.FileUtils; import com.github.zhanhb.ckfinder.connector.utils.ImageUtils; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.multipart.support.StandardServletMultipartResolver; /** * Class to handle <code>FileUpload</code> command. * */ @Slf4j public class FileUploadCommand extends BaseCommand<FileUploadParameter> implements IPostCommand { /** * Array containing unsafe characters which can't be used in file name. */ private static final Pattern UNSAFE_FILE_NAME_PATTERN = Pattern.compile("[:*?|/]"); /** * Executes file upload command. * * @param param the parameter * @param request request * @param response * @param context ckfinder context * @throws ConnectorException when error occurs. * @throws IOException when IO Exception occurs. */ @Override @SuppressWarnings("FinalMethod") final void execute(FileUploadParameter param, HttpServletRequest request, HttpServletResponse response, CKFinderContext context) throws ConnectorException, IOException { String errorMsg = ""; try { checkParam(param); // set in method initParams uploadFile(request, param, context); param.setUploaded(true); checkParam(param); // set in method uploadFile } catch (ConnectorException ex) { param.setErrorCode(ex.getErrorCode()); errorMsg = ex.getMessage(); } errorMsg = errorMsg.replace("%1", param.getNewFileName()); String path = ""; if (!param.isUploaded()) { param.setNewFileName(""); param.setCurrentFolder(""); } else { path = param.getType().getUrl() + param.getCurrentFolder(); } setContentType(param, response); PrintWriter writer = response.getWriter(); if ("txt".equals(param.getResponseType())) { writer.write(param.getNewFileName() + "|" + errorMsg); } else if (checkFuncNum(param)) { handleOnUploadCompleteCallFuncResponse(writer, errorMsg, path, param); } else { handleOnUploadCompleteResponse(writer, errorMsg, param); } writer.flush(); } /** * check if func num is set in request. * * @param param the parameter * @return true if is. */ protected boolean checkFuncNum(FileUploadParameter param) { return param.getCkFinderFuncNum() != null; } /** * return response when func num is set. * * @param out response. * @param errorMsg error message * @param path path * @param param the parameter * @throws IOException when IO Exception occurs. */ protected void handleOnUploadCompleteCallFuncResponse(Writer out, String errorMsg, String path, FileUploadParameter param) throws IOException { param.setCkFinderFuncNum(param.getCkFinderFuncNum().replaceAll("[^\\d]", "")); out.write("<script type=\"text/javascript\">window.parent.CKFinder.tools.callFunction(" + param.getCkFinderFuncNum() + ", '" + path + FileUtils.escapeJavaScript(param.getNewFileName()) + "', '" + errorMsg + "');</script>"); } /** * * @param writer out put stream * @param errorMsg error message * @param param the parameter * @throws IOException when IO Exception occurs. */ protected void handleOnUploadCompleteResponse(Writer writer, String errorMsg, FileUploadParameter param) throws IOException { writer.write("<script type=\"text/javascript\">window.parent.OnUploadCompleted('" + FileUtils.escapeJavaScript(param.getNewFileName()) + "', '" + (param.getErrorCode() != null ? errorMsg : "") + "');</script>"); } /** * initializing parameters for command handler. * * @param request request * @param context ckfinder context. * @return the parameter */ @Override protected FileUploadParameter popupParams(HttpServletRequest request, CKFinderContext context) { FileUploadParameter param = new FileUploadParameter(); try { doInitParam(param, request, context); } catch (ConnectorException ex) { param.setErrorCode(ex.getErrorCode()); } param.setCkFinderFuncNum(request.getParameter("CKFinderFuncNum")); param.setCkEditorFuncNum(request.getParameter("CKEditorFuncNum")); param.setResponseType(request.getParameter("response_type") != null ? request.getParameter("response_type") : request.getParameter("responseType")); param.setLangCode(request.getParameter("langCode")); if (param.getType() == null) { param.setErrorCode(ErrorCode.INVALID_TYPE); } return param; } /** * uploads file and saves to file. * * @param request request * @param param the parameter * @param context ckfinder context * @throws ConnectorException when error occurs */ private void uploadFile(HttpServletRequest request, FileUploadParameter param, CKFinderContext context) throws ConnectorException { if (!context.getAccessControl().hasPermission(param.getType().getName(), param.getCurrentFolder(), param.getUserRole(), AccessControl.FILE_UPLOAD)) { param.throwException(ErrorCode.UNAUTHORIZED); } fileUpload(request, param, context); } /** * * @param request http request * @param param the parameter * @param context ckfinder context * @throws ConnectorException when error occurs */ private void fileUpload(HttpServletRequest request, FileUploadParameter param, CKFinderContext context) throws ConnectorException { MultipartResolver multipartResolver = StandardHolder.RESOLVER; try { boolean multipart = multipartResolver.isMultipart(request); if (multipart) { log.debug("prepare resolveMultipart"); MultipartHttpServletRequest resolveMultipart = multipartResolver.resolveMultipart(request); log.debug("finish resolveMultipart"); try { Collection<MultipartFile> parts = resolveMultipart.getFileMap().values(); for (MultipartFile part : parts) { Path path = getPath(param.getType().getPath(), param.getCurrentFolder()); param.setFileName(getFileItemName(part)); validateUploadItem(part, path, param, context); saveTemporaryFile(path, part, param, context); return; } } finally { multipartResolver.cleanupMultipart(resolveMultipart); } } param.throwException("No file provided in the request."); } catch (MultipartException e) { log.debug("catch MultipartException", e); param.throwException(ErrorCode.UPLOADED_TOO_BIG); } catch (IOException e) { log.debug("catch IOException", e); param.throwException(ErrorCode.ACCESS_DENIED); } } /** * saves temporary file in the correct file path. * * @param path path to save file * @param item file upload item * @param param the parameter * @param context ckfinder context * @throws IOException when IO Exception occurs. * @throws ConnectorException when error occurs */ private void saveTemporaryFile(Path path, MultipartFile item, FileUploadParameter param, CKFinderContext context) throws IOException, ConnectorException { Path file = getPath(path, param.getNewFileName()); if (ImageUtils.isImageExtension(file)) { if (!context.isCheckSizeAfterScaling() && !ImageUtils.checkImageSize(item, context)) { param.throwException(ErrorCode.UPLOADED_TOO_BIG); } ImageUtils.createTmpThumb(item, file, getFileItemName(item), context); if (context.isCheckSizeAfterScaling() && !FileUtils.isFileSizeInRange(param.getType(), Files.size(file))) { Files.deleteIfExists(file); param.throwException(ErrorCode.UPLOADED_TOO_BIG); } } else { try (InputStream in = item.getInputStream()) { Files.copy(in, file); } } FileUploadEvent args = new FileUploadEvent(param.getCurrentFolder(), file); context.fireOnFileUpload(args); } /** * if file exists this method adds (number) to file. * * @param path folder * @param param the parameter * @return new file name. */ private String getFinalFileName(Path path, FileUploadParameter param) { String name = param.getNewFileName(); Path file = getPath(path, name); String nameWithoutExtension = FileUtils.getNameWithoutLongExtension(name); if (Files.exists(file) || isProtectedName(nameWithoutExtension)) { @SuppressWarnings("StringBufferWithoutInitialCapacity") StringBuilder sb = new StringBuilder(nameWithoutExtension).append("("); String suffix = ")." + FileUtils.getLongExtension(name); int len = sb.length(); int number = 0; do { number++; sb.append(number).append(suffix); name = sb.toString(); sb.setLength(len); file = getPath(path, name); } while (Files.exists(file)); param.setErrorCode(ErrorCode.UPLOADED_FILE_RENAMED); param.setNewFileName(name); } return name; } /** * validates uploaded file. * * @param item uploaded item. * @param path file path * @param param the parameter * @param context ckfinder context * @throws ConnectorException when error occurs */ private void validateUploadItem(MultipartFile item, Path path, FileUploadParameter param, CKFinderContext context) throws ConnectorException { if (item.getOriginalFilename() == null || item.getOriginalFilename().length() <= 0) { param.throwException(ErrorCode.UPLOADED_INVALID); } param.setFileName(getFileItemName(item)); param.setNewFileName(param.getFileName()); param.setNewFileName(UNSAFE_FILE_NAME_PATTERN.matcher(param.getNewFileName()).replaceAll("_")); if (context.isDisallowUnsafeCharacters()) { param.setNewFileName(param.getNewFileName().replace(';', '_')); } if (context.isForceAscii()) { param.setNewFileName(FileUtils.convertToAscii(param.getNewFileName())); } if (!param.getNewFileName().equals(param.getFileName())) { param.setErrorCode(ErrorCode.UPLOADED_INVALID_NAME_RENAMED); } if (context.isDirectoryHidden(param.getCurrentFolder())) { param.throwException(ErrorCode.INVALID_REQUEST); } if (!FileUtils.isFileNameValid(param.getNewFileName()) || context.isFileHidden(param.getNewFileName())) { param.throwException(ErrorCode.INVALID_NAME); } final ResourceType resourceType = param.getType(); if (!FileUtils.isFileExtensionAllowed(param.getNewFileName(), resourceType)) { param.throwException(ErrorCode.INVALID_EXTENSION); } if (context.isCheckDoubleFileExtensions()) { param.setNewFileName(FileUtils.renameFileWithBadExt(resourceType, param.getNewFileName())); } Path file = getPath(path, getFinalFileName(path, param)); if ((!ImageUtils.isImageExtension(file) || !context.isCheckSizeAfterScaling()) && !FileUtils.isFileSizeInRange(resourceType, item.getSize())) { param.throwException(ErrorCode.UPLOADED_TOO_BIG); } if (context.isSecureImageUploads() && ImageUtils.isImageExtension(file) && !ImageUtils.isValid(item)) { param.throwException(ErrorCode.UPLOADED_CORRUPT); } } /** * save if uploaded file item name is full file path not only file name. * * @param item file upload item * @return file name of uploaded item */ private String getFileItemName(MultipartFile item) { Pattern p = Pattern.compile("[^\\\\/]+$"); Matcher m = p.matcher(item.getOriginalFilename()); return m.find() ? m.group() : ""; } private boolean isProtectedName(String nameWithoutExtension) { return Pattern.compile("^(AUX|COM\\d|CLOCK\\$|CON|NUL|PRN|LPT\\d)$", Pattern.CASE_INSENSITIVE) .matcher(nameWithoutExtension).matches(); } void setContentType(FileUploadParameter param, HttpServletResponse response) { response.setContentType("text/html;charset=UTF-8"); } private void checkParam(FileUploadParameter param) throws ConnectorException { ErrorCode code = param.getErrorCode(); if (code != null) { param.throwException(code); } } @SuppressWarnings("UtilityClassWithoutPrivateConstructor") private static class StandardHolder { static final MultipartResolver RESOLVER = new StandardServletMultipartResolver(); } }