Java tutorial
/* * Copyright Bruce Liang (ldcsaa@gmail.com) * * Version : JessMA 3.5.1 * Author : Bruce Liang * Website : http://www.jessma.org * Project : http://www.oschina.net/p/portal-basic * Blog : http://www.cnblogs.com/ldcsaa * WeiBo : http://weibo.com/u/1402935851 * QQ Group : 75375912 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jessma.util.http; import java.io.File; import java.io.FileNotFoundException; import java.io.UnsupportedEncodingException; import java.lang.reflect.Array; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException; import org.apache.commons.fileupload.FileUploadBase.IOFileUploadException; import org.apache.commons.fileupload.FileUploadBase.InvalidContentTypeException; import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.ProgressListener; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.FileCleaningTracker; import org.jessma.util.GeneralHelper; import org.jessma.util.LStrSet; /** */ public class FileUploader { /** ??? Size Max ? */ public static final long NO_LIMIT_SIZE_MAX = -1; /** ???? File Size Max ? */ public static final long NO_LIMIT_FILE_SIZE_MAX = -1; /** */ public static final int DEFAULT_SIZE_THRESHOLD = DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD; /** ??? */ public static final FileNameGenerator DEFAULT_FILE_NAME_GENERATOR = new CommonFileNameGenerator(); private String savePath; private long sizeMax = NO_LIMIT_SIZE_MAX; private long fileSizeMax = NO_LIMIT_FILE_SIZE_MAX; private Set<String> acceptTypes = new LStrSet(); private Map<String, String[]> paramFields = new HashMap<String, String[]>(); private Map<String, FileInfo[]> fileFields = new HashMap<String, FileInfo[]>(); private FileNameGenerator fileNameGenerator = DEFAULT_FILE_NAME_GENERATOR; private int factorySizeThreshold = DEFAULT_SIZE_THRESHOLD; private String factoryRepository; private FileCleaningTracker factoryCleaningTracker; private String servletHeaderencoding; private ProgressListener servletProgressListener; private Throwable cause; public FileUploader() { } /** * * @param savePath : ??????{@link FileUploader#setSavePath(String)} */ public FileUploader(String savePath) { this(savePath, null); } /** * * @param savePath : ??????{@link FileUploader#setSavePath(String)} * @param sizeMax : ??{@link FileUploader#NO_LIMIT_SIZE_MAX} * @param fileSizeMax : ???{@link FileUploader#NO_LIMIT_FILE_SIZE_MAX} */ public FileUploader(String savePath, long sizeMax, long fileSizeMax) { this(savePath, null, sizeMax, fileSizeMax); } /** * * @param savePath : ??????{@link FileUploader#setSavePath(String)} * @param acceptTypes : ????? */ public FileUploader(String savePath, String[] acceptTypes) { this(savePath, acceptTypes, NO_LIMIT_SIZE_MAX, NO_LIMIT_FILE_SIZE_MAX); } /** * * @param savePath : ??????{@link FileUploader#setSavePath(String)} * @param acceptTypes : ????? * @param sizeMax : ??{@link FileUploader#NO_LIMIT_SIZE_MAX} * @param fileSizeMax : ???{@link FileUploader#NO_LIMIT_FILE_SIZE_MAX} */ public FileUploader(String savePath, String[] acceptTypes, long sizeMax, long fileSizeMax) { this.savePath = savePath; this.sizeMax = sizeMax; this.fileSizeMax = fileSizeMax; if (acceptTypes != null) setAcceptTypes(acceptTypes); } /** ??????*/ public String getSavePath() { return savePath; } /** ????? * * @param savePath : ??<br> * 1) ?'/'?'D:\'?<br> * 2) ? WEB ? Context mydir * '${WEB-APP-DIR}/mydir'<br> * 3) ?????? * {@link Result#INVALID_SAVE_PATH} * */ public void setSavePath(String savePath) { this.savePath = savePath; } /** ???? */ public long getFileSizeMax() { return fileSizeMax; } /** ??? */ public void setFileSizeMax(long fileSizeMax) { this.fileSizeMax = fileSizeMax; } /** ??? */ public long getSizeMax() { return sizeMax; } /** ?? */ public void setSizeMax(long sizeMax) { this.sizeMax = sizeMax; } /** ???? */ public Set<String> getAcceptTypes() { return acceptTypes; } /** ??? */ public void setAcceptTypes(Set<String> acceptTypes) { this.acceptTypes.clear(); for (String type : acceptTypes) addAcceptType(type); } /** ??? */ public void setAcceptTypes(String[] acceptTypes) { this.acceptTypes.clear(); for (String type : acceptTypes) addAcceptType(type); } /** ?? */ public boolean addAcceptType(String acceptType) { acceptType = adjustAcceptType(acceptType); if (acceptType.length() > 1) return this.acceptTypes.add(acceptType); return false; } /** ?? */ public boolean removeAcceptType(String acceptType) { acceptType = adjustAcceptType(acceptType); if (acceptType.length() > 1) return this.acceptTypes.remove(acceptType); return false; } private String adjustAcceptType(String acceptType) { int index = acceptType.lastIndexOf("."); if (index != -1) acceptType = acceptType.substring(index, acceptType.length()); else acceptType = "." + acceptType; return acceptType; } /** ??? * * @return : key -> ???value -> ? {@link String}[ ] * */ public Map<String, String[]> getParamFields() { return paramFields; } private void addParamField(String name, String value) { String[] array = paramFields.get(name); array = addField(array, name, value); paramFields.put(name, array); } @SuppressWarnings("unchecked") private <T> T[] addField(T[] array, String name, T value) { if (array == null) { array = (T[]) Array.newInstance(value.getClass(), 1); array[0] = value; } else { T[] array2 = (T[]) Array.newInstance(value.getClass(), array.length + 1); System.arraycopy(array, 0, array2, 0, array.length); array2[array.length] = value; array = array2; } return array; } /** ?? * * @return : key -> ???value -> {@link FileInfo}[ ] * */ public Map<String, FileInfo[]> getFileFields() { return fileFields; } private void addFileField(String name, FileInfo value) { FileInfo[] array = fileFields.get(name); array = addField(array, name, value); fileFields.put(name, array); } /** ???{@link DiskFileItemFactory#getRepository()} */ public String getFactoryRepository() { return factoryRepository; } /** ??{@link DiskFileItemFactory#setRepository(File)} */ public void setFactoryRepository(String factoryRepository) { this.factoryRepository = factoryRepository; } /** ???{@link DiskFileItemFactory#getSizeThreshold()} */ public int getFactorySizeThreshold() { return factorySizeThreshold; } /** ??{@link DiskFileItemFactory#setSizeThreshold(int)} */ public void setFactorySizeThreshold(int factorySizeThreshold) { this.factorySizeThreshold = factorySizeThreshold; } /** ??{@link DiskFileItemFactory#getFileCleaningTracker()} */ public FileCleaningTracker getFactoryCleaningTracker() { return factoryCleaningTracker; } /** ?{@link DiskFileItemFactory#setFileCleaningTracker(FileCleaningTracker)} */ public void setFactoryCleaningTracker(FileCleaningTracker factoryCleaningTracker) { this.factoryCleaningTracker = factoryCleaningTracker; } /** ?????{@link ServletFileUpload#getHeaderEncoding()} */ public String getServletHeaderencoding() { return servletHeaderencoding; } /** ????{@link ServletFileUpload#setHeaderEncoding(String)} */ public void setServletHeaderencoding(String servletHeaderencoding) { this.servletHeaderencoding = servletHeaderencoding; } /** ????{@link ServletFileUpload#getProgressListener()} */ public ProgressListener getServletProgressListener() { return servletProgressListener; } /** ???{@link ServletFileUpload#setProgressListener(ProgressListener)} */ public void setServletProgressListener(ProgressListener servletProgressListener) { this.servletProgressListener = servletProgressListener; } /** ?????{@link FileNameGenerator} */ public FileNameGenerator getFileNameGenerator() { return fileNameGenerator; } /** ????{@link FileNameGenerator} */ public void setFileNameGenerator(FileNameGenerator fileNameGenerator) { this.fileNameGenerator = fileNameGenerator; } /** ? */ public Throwable getCause() { return cause; } private void reset() { cause = null; fileFields.clear(); paramFields.clear(); } private void clean(List<FileItemInfo> fiis, int count) { reset(); for (int i = 0; i < count; i++) { File file = fiis.get(i).file; try { file.delete(); } catch (SecurityException e) { } } } /** * * @param request : {@link HttpServletRequest} * @param response : {@link HttpServletResponse} * * @return : ? {@link Result#SUCCESS} * {@link FileUploader#getCause()} ? * */ public Result upload(HttpServletRequest request, HttpServletResponse response) { reset(); String absolutePath = getAbsoluteSavePath(request); if (absolutePath == null) { cause = new FileNotFoundException(String.format("path '%s' not found or is not directory", savePath)); return Result.INVALID_SAVE_PATH; } ServletFileUpload sfu = getFileUploadComponent(); List<FileItemInfo> fiis = new ArrayList<FileItemInfo>(); List<FileItem> items = null; Result result = Result.SUCCESS; String encoding = servletHeaderencoding != null ? servletHeaderencoding : request.getCharacterEncoding(); FileNameGenerator fnGenerator = fileNameGenerator != null ? fileNameGenerator : DEFAULT_FILE_NAME_GENERATOR; try { items = (List<FileItem>) sfu.parseRequest(request); } catch (FileUploadException e) { cause = e; if (e instanceof FileSizeLimitExceededException) result = Result.FILE_SIZE_EXCEEDED; else if (e instanceof SizeLimitExceededException) result = Result.SIZE_EXCEEDED; else if (e instanceof InvalidContentTypeException) result = Result.INVALID_CONTENT_TYPE; else if (e instanceof IOFileUploadException) result = Result.FILE_UPLOAD_IO_EXCEPTION; else result = Result.OTHER_PARSE_REQUEST_EXCEPTION; } if (result == Result.SUCCESS) { result = parseFileItems(items, fnGenerator, absolutePath, encoding, fiis); if (result == Result.SUCCESS) result = writeFiles(fiis); } return result; } private Result writeFiles(List<FileItemInfo> fiis) { for (int i = 0; i < fiis.size(); i++) { FileItemInfo fii = fiis.get(i); try { fii.item.write(fii.file); } catch (Exception e) { clean(fiis, i); cause = e; return Result.WRITE_FILE_FAIL; } } return Result.SUCCESS; } private Result parseFileItems(List<FileItem> items, FileNameGenerator fnGenerator, String absolutePath, String encoding, List<FileItemInfo> fiis) { for (FileItem item : items) { if (item.isFormField()) parseFormField(item, encoding); else { if (GeneralHelper.isStrEmpty(item.getName())) continue; Result result = parseFileField(item, absolutePath, fnGenerator, fiis); if (result != Result.SUCCESS) { reset(); cause = new InvalidParameterException(String.format("file '%s' not accepted", item.getName())); return result; } } } return Result.SUCCESS; } private Result parseFileField(FileItem item, String absolutePath, FileNameGenerator fnGenerator, List<FileItemInfo> fiis) { String suffix = null; String uploadFileName = item.getName(); boolean isAcceptType = acceptTypes.isEmpty(); int stuffPos = uploadFileName.lastIndexOf("."); if (stuffPos != -1) { suffix = uploadFileName.substring(stuffPos, uploadFileName.length()).toLowerCase(); if (!isAcceptType) isAcceptType = acceptTypes.contains(suffix); } if (!isAcceptType) return Result.INVALID_FILE_TYPE; String saveFileName = fnGenerator.generate(item, suffix); if (suffix != null && !saveFileName.endsWith(suffix)) saveFileName += suffix; String fullFileName = absolutePath + File.separator + saveFileName; File saveFile = new File(fullFileName); FileInfo info = new FileInfo(uploadFileName, saveFile); fiis.add(new FileItemInfo(item, saveFile)); addFileField(item.getFieldName(), info); return Result.SUCCESS; } private void parseFormField(FileItem item, String encoding) { String name = item.getFieldName(); String value = item.getString(); if (!GeneralHelper.isStrEmpty(value) && encoding != null) { try { value = new String(value.getBytes("ISO-8859-1"), encoding); } catch (UnsupportedEncodingException e) { } } addParamField(name, value); } private ServletFileUpload getFileUploadComponent() { DiskFileItemFactory dif = new DiskFileItemFactory(); if (factorySizeThreshold != DEFAULT_SIZE_THRESHOLD) dif.setSizeThreshold(factorySizeThreshold); if (factoryRepository != null) dif.setRepository(new File(factoryRepository)); if (factoryCleaningTracker != null) dif.setFileCleaningTracker(factoryCleaningTracker); ServletFileUpload sfu = new ServletFileUpload(dif); if (sizeMax != NO_LIMIT_SIZE_MAX) sfu.setSizeMax(sizeMax); if (fileSizeMax != NO_LIMIT_FILE_SIZE_MAX) sfu.setFileSizeMax(fileSizeMax); if (servletHeaderencoding != null) sfu.setHeaderEncoding(servletHeaderencoding); if (servletProgressListener != null) sfu.setProgressListener(servletProgressListener); return sfu; } private String getAbsoluteSavePath(HttpServletRequest request) { String path = null; File file = new File(savePath); if (!file.isAbsolute()) file = new File(HttpHelper.getRequestRealPath(request, savePath)); if (file.isDirectory()) path = file.getAbsolutePath(); else if (!file.exists()) { try { synchronized (FileUploader.class) { if (file.exists() || file.mkdirs()) path = file.getAbsolutePath(); } } catch (SecurityException e) { } } return path; } /** ???? * * ????? {@link FileNameGenerator#generate} ????? * */ public static interface FileNameGenerator { /** ??? * * @param item : {@link FileItem} * @param suffix : ??? * */ String generate(FileItem item, String suffix); } /** ??? * * {@link FileNameGenerator} ?????? * */ public static class CommonFileNameGenerator implements FileNameGenerator { private static final int MAX_SERIAL = 999999; private static final AtomicInteger atomic = new AtomicInteger(); private static int getNextInteger() { int value = atomic.incrementAndGet(); if (value >= MAX_SERIAL) atomic.set(0); return value; } /** ??? 'XXXXXX_YYYYYYYYYYYYY' ??? */ @Override public String generate(FileItem item, String suffix) { int serial = getNextInteger(); long millsec = System.currentTimeMillis(); return String.format("%06d_%013d", serial, millsec); } } /** ? */ public static class FileInfo { private String uploadFileName; private File saveFile; FileInfo() { } FileInfo(String uploadFileName, File saveFile) { this.uploadFileName = uploadFileName; this.saveFile = saveFile; } /** ??? * * ???????? * */ public String getUploadFileName() { return uploadFileName; } /** ????? */ public String getUploadFileSimpleName() { if (uploadFileName != null) { String path = uploadFileName; if (!GeneralHelper.isWindowsPlatform()) path = path.replace('\\', File.separatorChar); return new File(path).getName(); } return null; } void setUploadFileName(String uploadFileName) { this.uploadFileName = uploadFileName; } /** ?? {@link File} */ public File getSaveFile() { return saveFile; } void setSaveFile(File saveFile) { this.saveFile = saveFile; } } private class FileItemInfo { FileItem item; File file; public FileItemInfo(FileItem item, File file) { this.item = item; this.file = file; } } /** */ public static enum Result { /** ? */ SUCCESS, /** ?? */ SIZE_EXCEEDED, /** ??? */ FILE_SIZE_EXCEEDED, /** ?? */ INVALID_CONTENT_TYPE, /** IO */ FILE_UPLOAD_IO_EXCEPTION, /** ? */ OTHER_PARSE_REQUEST_EXCEPTION, /** ? */ INVALID_FILE_TYPE, /** */ WRITE_FILE_FAIL, /** ?? */ INVALID_SAVE_PATH; } }