| @@ -32,6 +32,11 @@ | |||||
| <artifactId>weixin-java-open</artifactId> | <artifactId>weixin-java-open</artifactId> | ||||
| <version>${weixin-java-open.version}</version> | <version>${weixin-java-open.version}</version> | ||||
| </dependency> | </dependency> | ||||
| <dependency> | |||||
| <groupId>commons-fileupload</groupId> | |||||
| <artifactId>commons-fileupload</artifactId> | |||||
| <version>1.3.3</version> | |||||
| </dependency> | |||||
| </dependencies> | </dependencies> | ||||
| <build> | <build> | ||||
| @@ -7,12 +7,23 @@ import com.fasterxml.jackson.databind.ObjectMapper; | |||||
| import com.fasterxml.jackson.databind.module.SimpleModule; | import com.fasterxml.jackson.databind.module.SimpleModule; | ||||
| import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; | ||||
| import com.iformall.interceptor.RequestInterceptor; | import com.iformall.interceptor.RequestInterceptor; | ||||
| import com.iformall.ueditor.ActionEnter; | |||||
| import com.iformall.ueditor.ConfigManager; | |||||
| import com.iformall.ueditor.UEditorConfig; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | |||||
| import org.springframework.boot.context.properties.EnableConfigurationProperties; | |||||
| import org.springframework.context.annotation.Bean; | |||||
| import org.springframework.context.annotation.Configuration; | import org.springframework.context.annotation.Configuration; | ||||
| import org.springframework.http.converter.HttpMessageConverter; | import org.springframework.http.converter.HttpMessageConverter; | ||||
| import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; | ||||
| import org.springframework.web.cors.CorsConfiguration; | |||||
| import org.springframework.web.cors.UrlBasedCorsConfigurationSource; | |||||
| import org.springframework.web.filter.CharacterEncodingFilter; | |||||
| import org.springframework.web.filter.CorsFilter; | |||||
| import org.springframework.web.servlet.config.annotation.*; | import org.springframework.web.servlet.config.annotation.*; | ||||
| import javax.servlet.Filter; | |||||
| import java.math.BigDecimal; | import java.math.BigDecimal; | ||||
| import java.math.BigInteger; | import java.math.BigInteger; | ||||
| import java.text.SimpleDateFormat; | import java.text.SimpleDateFormat; | ||||
| @@ -20,10 +31,49 @@ import java.util.List; | |||||
| @Configuration | @Configuration | ||||
| @EnableWebMvc | @EnableWebMvc | ||||
| @EnableConfigurationProperties({UEditorConfig.class, AwsProperty.class}) | |||||
| public class WebConfig implements WebMvcConfigurer { | public class WebConfig implements WebMvcConfigurer { | ||||
| @Autowired | |||||
| private UEditorConfig uEditorConfig; | |||||
| @Autowired | |||||
| private AwsProperty awsProperty; | |||||
| @Autowired | @Autowired | ||||
| private RequestInterceptor requestInterceptor; | private RequestInterceptor requestInterceptor; | ||||
| @Bean | |||||
| @ConditionalOnMissingBean(ActionEnter.class) | |||||
| public ActionEnter actionEnter() { | |||||
| ActionEnter actionEnter = new ActionEnter(ConfigManager.getInstance(uEditorConfig, awsProperty)); | |||||
| return actionEnter; | |||||
| } | |||||
| @Bean | |||||
| public CorsFilter corsFilter() { | |||||
| final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource(); | |||||
| final CorsConfiguration corsConfiguration = new CorsConfiguration(); | |||||
| corsConfiguration.setAllowCredentials(true); | |||||
| corsConfiguration.addAllowedOrigin("*"); | |||||
| corsConfiguration.addAllowedHeader("*"); | |||||
| corsConfiguration.addAllowedMethod("*"); | |||||
| urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration); | |||||
| return new CorsFilter(urlBasedCorsConfigurationSource); | |||||
| } | |||||
| /** | |||||
| * 用于处理编码问题 | |||||
| * | |||||
| * @return | |||||
| */ | |||||
| @Bean | |||||
| public Filter characterEncodingFilter() { | |||||
| CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); | |||||
| characterEncodingFilter.setEncoding("UTF-8"); | |||||
| characterEncodingFilter.setForceEncoding(true); | |||||
| return characterEncodingFilter; | |||||
| } | |||||
| @Override | @Override | ||||
| public void addInterceptors(InterceptorRegistry registry) { | public void addInterceptors(InterceptorRegistry registry) { | ||||
| registry.addInterceptor(requestInterceptor).addPathPatterns("/**"); | registry.addInterceptor(requestInterceptor).addPathPatterns("/**"); | ||||
| @@ -35,6 +85,9 @@ public class WebConfig implements WebMvcConfigurer { | |||||
| .addResourceLocations("classpath:/META-INF/resources/"); | .addResourceLocations("classpath:/META-INF/resources/"); | ||||
| registry.addResourceHandler("/webjars/**") | registry.addResourceHandler("/webjars/**") | ||||
| .addResourceLocations("classpath:/META-INF/resources/webjars/"); | .addResourceLocations("classpath:/META-INF/resources/webjars/"); | ||||
| // ueditor | |||||
| registry.addResourceHandler("/upload/**") | |||||
| .addResourceLocations("file:" + uEditorConfig.getUploadPath()); | |||||
| //registry.addResourceHandler("/app/**").addResourceLocations("classpath:/app/"); | //registry.addResourceHandler("/app/**").addResourceLocations("classpath:/app/"); | ||||
| } | } | ||||
| @@ -0,0 +1,118 @@ | |||||
| package com.iformall.ueditor; | |||||
| import com.iformall.ueditor.define.ActionMap; | |||||
| import com.iformall.ueditor.define.AppInfo; | |||||
| import com.iformall.ueditor.define.BaseState; | |||||
| import com.iformall.ueditor.define.State; | |||||
| import com.iformall.ueditor.hunter.FileManager; | |||||
| import com.iformall.ueditor.hunter.ImageHunter; | |||||
| import com.iformall.ueditor.upload.Uploader; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import java.util.Map; | |||||
| public class ActionEnter { | |||||
| private HttpServletRequest request = null; | |||||
| private String actionType = null; | |||||
| private ConfigManager configManager = null; | |||||
| public ActionEnter(ConfigManager configManager) { | |||||
| this.configManager = configManager; | |||||
| } | |||||
| public String exec(HttpServletRequest request) { | |||||
| this.request = request; | |||||
| this.actionType = request.getParameter("action"); | |||||
| String callbackName = this.request.getParameter("callback"); | |||||
| if (callbackName != null) { | |||||
| if (!validCallbackName(callbackName)) { | |||||
| return new BaseState(false, AppInfo.ILLEGAL).toJSONString(); | |||||
| } | |||||
| return callbackName + "(" + this.invoke() + ");"; | |||||
| } else { | |||||
| return this.invoke(); | |||||
| } | |||||
| } | |||||
| public String invoke() { | |||||
| if (actionType == null || !ActionMap.mapping.containsKey(actionType)) { | |||||
| return new BaseState(false, AppInfo.INVALID_ACTION).toJSONString(); | |||||
| } | |||||
| if (this.configManager == null || !this.configManager.valid()) { | |||||
| return new BaseState(false, AppInfo.CONFIG_ERROR).toJSONString(); | |||||
| } | |||||
| State state = null; | |||||
| int actionCode = ActionMap.getType(this.actionType); | |||||
| Map<String, Object> conf = null; | |||||
| switch (actionCode) { | |||||
| case ActionMap.CONFIG: | |||||
| return this.configManager.getAllConfig().toString(); | |||||
| case ActionMap.UPLOAD_IMAGE: | |||||
| case ActionMap.UPLOAD_SCRAWL: | |||||
| case ActionMap.UPLOAD_VIDEO: | |||||
| case ActionMap.UPLOAD_FILE: | |||||
| conf = this.configManager.getConfig(actionCode); | |||||
| state = new Uploader(request, conf).doExec(); | |||||
| break; | |||||
| case ActionMap.CATCH_IMAGE: | |||||
| conf = configManager.getConfig(actionCode); | |||||
| String[] list = this.request.getParameterValues((String) conf.get("fieldName")); | |||||
| state = new ImageHunter(conf).capture(list); | |||||
| break; | |||||
| case ActionMap.LIST_IMAGE: | |||||
| case ActionMap.LIST_FILE: | |||||
| conf = configManager.getConfig(actionCode); | |||||
| int start = this.getStartIndex(); | |||||
| state = new FileManager(conf).listFile(start); | |||||
| break; | |||||
| } | |||||
| return state.toJSONString(); | |||||
| } | |||||
| public int getStartIndex() { | |||||
| String start = this.request.getParameter("start"); | |||||
| try { | |||||
| return Integer.parseInt(start); | |||||
| } catch (Exception e) { | |||||
| return 0; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * callback参数验证 | |||||
| */ | |||||
| public boolean validCallbackName(String name) { | |||||
| if (name.matches("^[a-zA-Z_]+[\\w0-9_]*$")) { | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,223 @@ | |||||
| package com.iformall.ueditor; | |||||
| import com.alibaba.fastjson.JSON; | |||||
| import com.alibaba.fastjson.JSONArray; | |||||
| import com.alibaba.fastjson.JSONException; | |||||
| import com.alibaba.fastjson.JSONObject; | |||||
| import com.iformall.config.AwsProperty; | |||||
| import com.iformall.ueditor.define.ActionMap; | |||||
| import java.io.*; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| import java.util.Set; | |||||
| /** | |||||
| * 配置管理器 | |||||
| * | |||||
| * @author hancong03@baidu.com | |||||
| */ | |||||
| public final class ConfigManager { | |||||
| private static final String configFileName = "config.json"; | |||||
| private JSONObject jsonConfig = null; | |||||
| // 涂鸦上传filename定义 | |||||
| private final static String SCRAWL_FILE_NAME = "scrawl"; | |||||
| // 远程图片抓取filename定义 | |||||
| private final static String REMOTE_FILE_NAME = "remote"; | |||||
| //配置信息 | |||||
| private UEditorConfig uEditorConfig; | |||||
| private AwsProperty awsProperty; | |||||
| /* | |||||
| * 通过一个给定的路径构建一个配置管理器, 该管理器要求地址路径所在目录下必须存在config.properties文件 | |||||
| */ | |||||
| private ConfigManager(UEditorConfig uEditorConfig, AwsProperty awsProperty) throws IOException { | |||||
| this.uEditorConfig = uEditorConfig; | |||||
| this.awsProperty = awsProperty; | |||||
| String configPath = uEditorConfig.getConfig(); | |||||
| configPath = configPath == null || configPath.isEmpty() ? configFileName : configPath; | |||||
| this.initEnv(configPath); | |||||
| } | |||||
| /** | |||||
| * 配置管理器构造工厂 | |||||
| * | |||||
| * @param uEditorConfig 配置文件 | |||||
| * @return 配置管理器实例或者null | |||||
| */ | |||||
| public static ConfigManager getInstance(UEditorConfig uEditorConfig, AwsProperty awsProperty) { | |||||
| try { | |||||
| return new ConfigManager(uEditorConfig, awsProperty); | |||||
| } catch (Exception e) { | |||||
| System.err.println("UEditor ConfigManager load error~"); | |||||
| return null; | |||||
| } | |||||
| } | |||||
| // 验证配置文件加载是否正确 | |||||
| public boolean valid() { | |||||
| return this.jsonConfig != null; | |||||
| } | |||||
| public JSONObject getAllConfig() { | |||||
| return this.jsonConfig; | |||||
| } | |||||
| public Map<String, Object> getConfig(int type) { | |||||
| Map<String, Object> conf = new HashMap<String, Object>(); | |||||
| String savePath = null; | |||||
| try { | |||||
| switch (type) { | |||||
| case ActionMap.UPLOAD_FILE: | |||||
| conf.put("isBase64", "false"); | |||||
| conf.put("maxSize", this.jsonConfig.getLong("fileMaxSize")); | |||||
| conf.put("allowFiles", this.getArray("fileAllowFiles")); | |||||
| conf.put("fieldName", this.jsonConfig.getString("fileFieldName")); | |||||
| conf.put("clientRegion", this.awsProperty.getClientRegion()); | |||||
| conf.put("bucketName", this.awsProperty.getBucketName()); | |||||
| conf.put("access", this.awsProperty.getAccess()); | |||||
| conf.put("secret", this.awsProperty.getSecret()); | |||||
| savePath = this.jsonConfig.getString("filePathFormat"); | |||||
| break; | |||||
| case ActionMap.UPLOAD_IMAGE: | |||||
| conf.put("isBase64", "false"); | |||||
| conf.put("maxSize", this.jsonConfig.getLong("imageMaxSize")); | |||||
| conf.put("allowFiles", this.getArray("imageAllowFiles")); | |||||
| conf.put("fieldName", this.jsonConfig.getString("imageFieldName")); | |||||
| conf.put("clientRegion", this.awsProperty.getClientRegion()); | |||||
| conf.put("bucketName", this.awsProperty.getBucketName()); | |||||
| conf.put("access", this.awsProperty.getAccess()); | |||||
| conf.put("secret", this.awsProperty.getSecret()); | |||||
| savePath = this.jsonConfig.getString("imagePathFormat"); | |||||
| break; | |||||
| case ActionMap.UPLOAD_VIDEO: | |||||
| conf.put("maxSize", this.jsonConfig.getLong("videoMaxSize")); | |||||
| conf.put("allowFiles", this.getArray("videoAllowFiles")); | |||||
| conf.put("fieldName", this.jsonConfig.getString("videoFieldName")); | |||||
| conf.put("clientRegion", this.awsProperty.getClientRegion()); | |||||
| conf.put("bucketName", this.awsProperty.getBucketName()); | |||||
| conf.put("access", this.awsProperty.getAccess()); | |||||
| conf.put("secret", this.awsProperty.getSecret()); | |||||
| savePath = this.jsonConfig.getString("videoPathFormat"); | |||||
| break; | |||||
| case ActionMap.UPLOAD_SCRAWL: | |||||
| conf.put("filename", ConfigManager.SCRAWL_FILE_NAME); | |||||
| conf.put("maxSize", this.jsonConfig.getLong("scrawlMaxSize")); | |||||
| conf.put("fieldName", this.jsonConfig.getString("scrawlFieldName")); | |||||
| conf.put("isBase64", "true"); | |||||
| savePath = this.jsonConfig.getString("scrawlPathFormat"); | |||||
| break; | |||||
| case ActionMap.CATCH_IMAGE: | |||||
| conf.put("filename", ConfigManager.REMOTE_FILE_NAME); | |||||
| conf.put("filter", this.getArray("catcherLocalDomain")); | |||||
| conf.put("maxSize", this.jsonConfig.getLong("catcherMaxSize")); | |||||
| conf.put("allowFiles", this.getArray("catcherAllowFiles")); | |||||
| conf.put("fieldName", this.jsonConfig.getString("catcherFieldName") + "[]"); | |||||
| savePath = this.jsonConfig.getString("catcherPathFormat"); | |||||
| break; | |||||
| case ActionMap.LIST_IMAGE: | |||||
| conf.put("allowFiles", this.getArray("imageManagerAllowFiles")); | |||||
| conf.put("dir", this.jsonConfig.getString("imageManagerListPath")); | |||||
| conf.put("count", this.jsonConfig.getIntValue("imageManagerListSize")); | |||||
| break; | |||||
| case ActionMap.LIST_FILE: | |||||
| conf.put("allowFiles", this.getArray("fileManagerAllowFiles")); | |||||
| conf.put("dir", this.jsonConfig.getString("fileManagerListPath")); | |||||
| conf.put("count", this.jsonConfig.getIntValue("fileManagerListSize")); | |||||
| break; | |||||
| } | |||||
| } catch (JSONException e) { | |||||
| e.printStackTrace(); | |||||
| } | |||||
| conf.put("savePath", savePath); | |||||
| conf.put("rootPath", uEditorConfig.getUploadPath()); | |||||
| conf.put("urlPrefix", uEditorConfig.getUrlPrefix()); | |||||
| return conf; | |||||
| } | |||||
| private void initEnv(String configPath) throws IOException { | |||||
| String configContent = this.readFile(configPath); | |||||
| try { | |||||
| JSONObject jsonConfig = JSON.parseObject(configContent); | |||||
| //统一url访问前缀 | |||||
| if (uEditorConfig.getUnified()) { | |||||
| Set<Map.Entry<String, Object>> entrySet = jsonConfig.entrySet(); | |||||
| for (Map.Entry<String, Object> entry : entrySet) { | |||||
| String key = entry.getKey(); | |||||
| if(key.contains("UrlPrefix")) { | |||||
| jsonConfig.put(key, uEditorConfig.getUrlPrefix()); | |||||
| } | |||||
| } | |||||
| } | |||||
| this.jsonConfig = jsonConfig; | |||||
| } catch (Exception e) { | |||||
| this.jsonConfig = null; | |||||
| } | |||||
| } | |||||
| private String[] getArray(String key) throws JSONException { | |||||
| JSONArray jsonArray = this.jsonConfig.getJSONArray(key); | |||||
| String[] result = new String[jsonArray.size()]; | |||||
| for (int i = 0, len = jsonArray.size(); i < len; i++) { | |||||
| result[i] = jsonArray.getString(i); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| private String readFile(String path) throws IOException { | |||||
| StringBuilder builder = new StringBuilder(); | |||||
| try { | |||||
| InputStream inputStream = getClass().getClassLoader().getResourceAsStream(path); | |||||
| InputStreamReader reader = new InputStreamReader(inputStream, "UTF-8"); | |||||
| BufferedReader bfReader = new BufferedReader(reader); | |||||
| String tmpContent = null; | |||||
| while ((tmpContent = bfReader.readLine()) != null) { | |||||
| builder.append(tmpContent); | |||||
| } | |||||
| bfReader.close(); | |||||
| } catch (UnsupportedEncodingException e) { | |||||
| // 忽略 | |||||
| } | |||||
| return this.filter(builder.toString()); | |||||
| } | |||||
| // 过滤输入字符串, 剔除多行注释以及替换掉反斜杠 | |||||
| private String filter(String input) { | |||||
| return input.replaceAll("/\\*[\\s\\S]*?\\*/", ""); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,24 @@ | |||||
| package com.iformall.ueditor; | |||||
| public class Encoder { | |||||
| public static String toUnicode ( String input ) { | |||||
| StringBuilder builder = new StringBuilder(); | |||||
| char[] chars = input.toCharArray(); | |||||
| for ( char ch : chars ) { | |||||
| if ( ch < 256 ) { | |||||
| builder.append( ch ); | |||||
| } else { | |||||
| builder.append( "\\u" + Integer.toHexString( ch& 0xffff ) ); | |||||
| } | |||||
| } | |||||
| return builder.toString(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,157 @@ | |||||
| package com.iformall.ueditor; | |||||
| import java.text.SimpleDateFormat; | |||||
| import java.util.Date; | |||||
| import java.util.regex.Matcher; | |||||
| import java.util.regex.Pattern; | |||||
| public class PathFormat { | |||||
| private static final String TIME = "time"; | |||||
| private static final String FULL_YEAR = "yyyy"; | |||||
| private static final String YEAR = "yy"; | |||||
| private static final String MONTH = "mm"; | |||||
| private static final String DAY = "dd"; | |||||
| private static final String HOUR = "hh"; | |||||
| private static final String MINUTE = "ii"; | |||||
| private static final String SECOND = "ss"; | |||||
| private static final String RAND = "rand"; | |||||
| private static Date currentDate = null; | |||||
| public static String parse ( String input ) { | |||||
| Pattern pattern = Pattern.compile( "\\{([^\\}]+)\\}", Pattern.CASE_INSENSITIVE ); | |||||
| Matcher matcher = pattern.matcher(input); | |||||
| PathFormat.currentDate = new Date(); | |||||
| StringBuffer sb = new StringBuffer(); | |||||
| while ( matcher.find() ) { | |||||
| matcher.appendReplacement(sb, PathFormat.getString( matcher.group( 1 ) ) ); | |||||
| } | |||||
| matcher.appendTail(sb); | |||||
| return sb.toString(); | |||||
| } | |||||
| /** | |||||
| * 格式化路径, 把windows路径替换成标准路径 | |||||
| * @param input 待格式化的路径 | |||||
| * @return 格式化后的路径 | |||||
| */ | |||||
| public static String format ( String input ) { | |||||
| return input.replace( "\\", "/" ); | |||||
| } | |||||
| public static String parse ( String input, String filename ) { | |||||
| Pattern pattern = Pattern.compile( "\\{([^\\}]+)\\}", Pattern.CASE_INSENSITIVE ); | |||||
| Matcher matcher = pattern.matcher(input); | |||||
| String matchStr = null; | |||||
| PathFormat.currentDate = new Date(); | |||||
| StringBuffer sb = new StringBuffer(); | |||||
| while ( matcher.find() ) { | |||||
| matchStr = matcher.group( 1 ); | |||||
| if ( matchStr.indexOf( "filename" ) != -1 ) { | |||||
| filename = filename.replace( "$", "\\$" ).replaceAll( "[\\/:*?\"<>|]", "" ); | |||||
| matcher.appendReplacement(sb, filename ); | |||||
| } else { | |||||
| matcher.appendReplacement(sb, PathFormat.getString( matchStr ) ); | |||||
| } | |||||
| } | |||||
| matcher.appendTail(sb); | |||||
| return sb.toString(); | |||||
| } | |||||
| private static String getString ( String pattern ) { | |||||
| pattern = pattern.toLowerCase(); | |||||
| // time 处理 | |||||
| if ( pattern.indexOf( PathFormat.TIME ) != -1 ) { | |||||
| return PathFormat.getTimestamp(); | |||||
| } else if ( pattern.indexOf( PathFormat.FULL_YEAR ) != -1 ) { | |||||
| return PathFormat.getFullYear(); | |||||
| } else if ( pattern.indexOf( PathFormat.YEAR ) != -1 ) { | |||||
| return PathFormat.getYear(); | |||||
| } else if ( pattern.indexOf( PathFormat.MONTH ) != -1 ) { | |||||
| return PathFormat.getMonth(); | |||||
| } else if ( pattern.indexOf( PathFormat.DAY ) != -1 ) { | |||||
| return PathFormat.getDay(); | |||||
| } else if ( pattern.indexOf( PathFormat.HOUR ) != -1 ) { | |||||
| return PathFormat.getHour(); | |||||
| } else if ( pattern.indexOf( PathFormat.MINUTE ) != -1 ) { | |||||
| return PathFormat.getMinute(); | |||||
| } else if ( pattern.indexOf( PathFormat.SECOND ) != -1 ) { | |||||
| return PathFormat.getSecond(); | |||||
| } else if ( pattern.indexOf( PathFormat.RAND ) != -1 ) { | |||||
| return PathFormat.getRandom( pattern ); | |||||
| } | |||||
| return pattern; | |||||
| } | |||||
| private static String getTimestamp () { | |||||
| return System.currentTimeMillis() + ""; | |||||
| } | |||||
| private static String getFullYear () { | |||||
| return new SimpleDateFormat( "yyyy" ).format( PathFormat.currentDate ); | |||||
| } | |||||
| private static String getYear () { | |||||
| return new SimpleDateFormat( "yy" ).format( PathFormat.currentDate ); | |||||
| } | |||||
| private static String getMonth () { | |||||
| return new SimpleDateFormat( "MM" ).format( PathFormat.currentDate ); | |||||
| } | |||||
| private static String getDay () { | |||||
| return new SimpleDateFormat( "dd" ).format( PathFormat.currentDate ); | |||||
| } | |||||
| private static String getHour () { | |||||
| return new SimpleDateFormat( "HH" ).format( PathFormat.currentDate ); | |||||
| } | |||||
| private static String getMinute () { | |||||
| return new SimpleDateFormat( "mm" ).format( PathFormat.currentDate ); | |||||
| } | |||||
| private static String getSecond () { | |||||
| return new SimpleDateFormat( "ss" ).format( PathFormat.currentDate ); | |||||
| } | |||||
| private static String getRandom ( String pattern ) { | |||||
| int length = 0; | |||||
| pattern = pattern.split( ":" )[ 1 ].trim(); | |||||
| length = Integer.parseInt( pattern ); | |||||
| return ( Math.random() + "" ).replace( ".", "" ).substring( 0, length ); | |||||
| } | |||||
| public static void main(String[] args) { | |||||
| // TODO Auto-generated method stub | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,59 @@ | |||||
| package com.iformall.ueditor; | |||||
| import org.springframework.boot.context.properties.ConfigurationProperties; | |||||
| /** | |||||
| * Created by pangxiaofeng on 2017/9/27. | |||||
| */ | |||||
| @ConfigurationProperties(prefix = "ueditor") | |||||
| public class UEditorConfig { | |||||
| /** | |||||
| * config.json的文件存放地址 | |||||
| */ | |||||
| private String config; | |||||
| /** | |||||
| * 是否同统一上传地址:图片上传地址,视频上传地址... | |||||
| */ | |||||
| private boolean unified; | |||||
| /** | |||||
| * 文件上传路径 | |||||
| */ | |||||
| private String uploadPath; | |||||
| /** | |||||
| * 文件url前缀 | |||||
| */ | |||||
| private String urlPrefix; | |||||
| public String getConfig() { | |||||
| return config; | |||||
| } | |||||
| public void setConfig(String config) { | |||||
| this.config = config; | |||||
| } | |||||
| public String getUploadPath() { | |||||
| return uploadPath; | |||||
| } | |||||
| public void setUploadPath(String uploadPath) { | |||||
| this.uploadPath = uploadPath; | |||||
| } | |||||
| public String getUrlPrefix() { | |||||
| return urlPrefix; | |||||
| } | |||||
| public void setUrlPrefix(String urlPrefix) { | |||||
| this.urlPrefix = urlPrefix; | |||||
| } | |||||
| public boolean getUnified() { | |||||
| return unified; | |||||
| } | |||||
| public void setUnified(boolean unified) { | |||||
| this.unified = unified; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,42 @@ | |||||
| package com.iformall.ueditor.define; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * 定义请求action类型 | |||||
| * @author hancong03@baidu.com | |||||
| * | |||||
| */ | |||||
| @SuppressWarnings("serial") | |||||
| public final class ActionMap { | |||||
| public static final Map<String, Integer> mapping; | |||||
| // 获取配置请求 | |||||
| public static final int CONFIG = 0; | |||||
| public static final int UPLOAD_IMAGE = 1; | |||||
| public static final int UPLOAD_SCRAWL = 2; | |||||
| public static final int UPLOAD_VIDEO = 3; | |||||
| public static final int UPLOAD_FILE = 4; | |||||
| public static final int CATCH_IMAGE = 5; | |||||
| public static final int LIST_FILE = 6; | |||||
| public static final int LIST_IMAGE = 7; | |||||
| static { | |||||
| mapping = new HashMap<String, Integer>(){{ | |||||
| put( "config", ActionMap.CONFIG ); | |||||
| put( "uploadimage", ActionMap.UPLOAD_IMAGE ); | |||||
| put( "uploadscrawl", ActionMap.UPLOAD_SCRAWL ); | |||||
| put( "uploadvideo", ActionMap.UPLOAD_VIDEO ); | |||||
| put( "uploadfile", ActionMap.UPLOAD_FILE ); | |||||
| put( "catchimage", ActionMap.CATCH_IMAGE ); | |||||
| put( "listfile", ActionMap.LIST_FILE ); | |||||
| put( "listimage", ActionMap.LIST_IMAGE ); | |||||
| }}; | |||||
| } | |||||
| public static int getType ( String key ) { | |||||
| return ActionMap.mapping.get( key ); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,5 @@ | |||||
| package com.iformall.ueditor.define; | |||||
| public enum ActionState { | |||||
| UNKNOW_ERROR | |||||
| } | |||||
| @@ -0,0 +1,77 @@ | |||||
| package com.iformall.ueditor.define; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| public final class AppInfo { | |||||
| public static final int SUCCESS = 0; | |||||
| public static final int MAX_SIZE = 1; | |||||
| public static final int PERMISSION_DENIED = 2; | |||||
| public static final int FAILED_CREATE_FILE = 3; | |||||
| public static final int IO_ERROR = 4; | |||||
| public static final int NOT_MULTIPART_CONTENT = 5; | |||||
| public static final int PARSE_REQUEST_ERROR = 6; | |||||
| public static final int NOTFOUND_UPLOAD_DATA = 7; | |||||
| public static final int NOT_ALLOW_FILE_TYPE = 8; | |||||
| public static final int INVALID_ACTION = 101; | |||||
| public static final int CONFIG_ERROR = 102; | |||||
| public static final int PREVENT_HOST = 201; | |||||
| public static final int CONNECTION_ERROR = 202; | |||||
| public static final int REMOTE_FAIL = 203; | |||||
| public static final int NOT_DIRECTORY = 301; | |||||
| public static final int NOT_EXIST = 302; | |||||
| public static final int ILLEGAL = 401; | |||||
| public static Map<Integer, String> info = new HashMap<Integer, String>(){{ | |||||
| put( AppInfo.SUCCESS, "SUCCESS" ); | |||||
| // 无效的Action | |||||
| put( AppInfo.INVALID_ACTION, "\u65E0\u6548\u7684Action" ); | |||||
| // 配置文件初始化失败 | |||||
| put( AppInfo.CONFIG_ERROR, "\u914D\u7F6E\u6587\u4EF6\u521D\u59CB\u5316\u5931\u8D25" ); | |||||
| // 抓取远程图片失败 | |||||
| put( AppInfo.REMOTE_FAIL, "\u6293\u53D6\u8FDC\u7A0B\u56FE\u7247\u5931\u8D25" ); | |||||
| // 被阻止的远程主机 | |||||
| put( AppInfo.PREVENT_HOST, "\u88AB\u963B\u6B62\u7684\u8FDC\u7A0B\u4E3B\u673A" ); | |||||
| // 远程连接出错 | |||||
| put( AppInfo.CONNECTION_ERROR, "\u8FDC\u7A0B\u8FDE\u63A5\u51FA\u9519" ); | |||||
| // "文件大小超出限制" | |||||
| put( AppInfo.MAX_SIZE, "\u6587\u4ef6\u5927\u5c0f\u8d85\u51fa\u9650\u5236" ); | |||||
| // 权限不足, 多指写权限 | |||||
| put( AppInfo.PERMISSION_DENIED, "\u6743\u9650\u4E0D\u8DB3" ); | |||||
| // 创建文件失败 | |||||
| put( AppInfo.FAILED_CREATE_FILE, "\u521B\u5EFA\u6587\u4EF6\u5931\u8D25" ); | |||||
| // IO错误 | |||||
| put( AppInfo.IO_ERROR, "IO\u9519\u8BEF" ); | |||||
| // 上传表单不是multipart/form-data类型 | |||||
| put( AppInfo.NOT_MULTIPART_CONTENT, "\u4E0A\u4F20\u8868\u5355\u4E0D\u662Fmultipart/form-data\u7C7B\u578B" ); | |||||
| // 解析上传表单错误 | |||||
| put( AppInfo.PARSE_REQUEST_ERROR, "\u89E3\u6790\u4E0A\u4F20\u8868\u5355\u9519\u8BEF" ); | |||||
| // 未找到上传数据 | |||||
| put( AppInfo.NOTFOUND_UPLOAD_DATA, "\u672A\u627E\u5230\u4E0A\u4F20\u6570\u636E" ); | |||||
| // 不允许的文件类型 | |||||
| put( AppInfo.NOT_ALLOW_FILE_TYPE, "\u4E0D\u5141\u8BB8\u7684\u6587\u4EF6\u7C7B\u578B" ); | |||||
| // 指定路径不是目录 | |||||
| put( AppInfo.NOT_DIRECTORY, "\u6307\u5B9A\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55" ); | |||||
| // 指定路径并不存在 | |||||
| put( AppInfo.NOT_EXIST, "\u6307\u5B9A\u8DEF\u5F84\u5E76\u4E0D\u5B58\u5728" ); | |||||
| // callback参数名不合法 | |||||
| put( AppInfo.ILLEGAL, "Callback\u53C2\u6570\u540D\u4E0D\u5408\u6CD5" ); | |||||
| }}; | |||||
| public static String getStateInfo ( int key ) { | |||||
| return AppInfo.info.get( key ); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,90 @@ | |||||
| package com.iformall.ueditor.define; | |||||
| import com.iformall.ueditor.Encoder; | |||||
| import java.util.HashMap; | |||||
| import java.util.Iterator; | |||||
| import java.util.Map; | |||||
| public class BaseState implements State { | |||||
| private boolean state = false; | |||||
| private String info = null; | |||||
| private Map<String, String> infoMap = new HashMap<String, String>(); | |||||
| public BaseState () { | |||||
| this.state = true; | |||||
| } | |||||
| public BaseState ( boolean state ) { | |||||
| this.setState( state ); | |||||
| } | |||||
| public BaseState ( boolean state, String info ) { | |||||
| this.setState( state ); | |||||
| this.info = info; | |||||
| } | |||||
| public BaseState ( boolean state, int infoCode ) { | |||||
| this.setState( state ); | |||||
| this.info = AppInfo.getStateInfo( infoCode ); | |||||
| } | |||||
| public boolean isSuccess () { | |||||
| return this.state; | |||||
| } | |||||
| public void setState ( boolean state ) { | |||||
| this.state = state; | |||||
| } | |||||
| public void setInfo ( String info ) { | |||||
| this.info = info; | |||||
| } | |||||
| public void setInfo ( int infoCode ) { | |||||
| this.info = AppInfo.getStateInfo( infoCode ); | |||||
| } | |||||
| @Override | |||||
| public String toJSONString() { | |||||
| return this.toString(); | |||||
| } | |||||
| public String toString () { | |||||
| String key = null; | |||||
| String stateVal = this.isSuccess() ? AppInfo.getStateInfo( AppInfo.SUCCESS ) : this.info; | |||||
| StringBuilder builder = new StringBuilder(); | |||||
| builder.append( "{\"state\": \"" + stateVal + "\"" ); | |||||
| Iterator<String> iterator = this.infoMap.keySet().iterator(); | |||||
| while ( iterator.hasNext() ) { | |||||
| key = iterator.next(); | |||||
| builder.append( ",\"" + key + "\": \"" + this.infoMap.get(key) + "\"" ); | |||||
| } | |||||
| builder.append( "}" ); | |||||
| return Encoder.toUnicode( builder.toString() ); | |||||
| } | |||||
| @Override | |||||
| public void putInfo(String name, String val) { | |||||
| this.infoMap.put(name, val); | |||||
| } | |||||
| @Override | |||||
| public void putInfo(String name, long val) { | |||||
| this.putInfo(name, val+""); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,31 @@ | |||||
| package com.iformall.ueditor.define; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| public class FileType { | |||||
| public static final String JPG = "JPG"; | |||||
| private static final Map<String, String> types = new HashMap<String, String>(){{ | |||||
| put( FileType.JPG, ".jpg" ); | |||||
| }}; | |||||
| public static String getSuffix ( String key ) { | |||||
| return FileType.types.get( key ); | |||||
| } | |||||
| /** | |||||
| * 根据给定的文件名,获取其后缀信息 | |||||
| * @param filename | |||||
| * @return | |||||
| */ | |||||
| public static String getSuffixByFilename ( String filename ) { | |||||
| return filename.substring( filename.lastIndexOf( "." ) ).toLowerCase(); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,20 @@ | |||||
| package com.iformall.ueditor.define; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| public class MIMEType { | |||||
| public static final Map<String, String> types = new HashMap<String, String>(){{ | |||||
| put( "image/gif", ".gif" ); | |||||
| put( "image/jpeg", ".jpg" ); | |||||
| put( "image/jpg", ".jpg" ); | |||||
| put( "image/png", ".png" ); | |||||
| put( "image/bmp", ".bmp" ); | |||||
| }}; | |||||
| public static String getSuffix ( String mime ) { | |||||
| return MIMEType.types.get( mime ); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,108 @@ | |||||
| package com.iformall.ueditor.define; | |||||
| import com.iformall.ueditor.Encoder; | |||||
| import java.util.*; | |||||
| /** | |||||
| * 多状态集合状态 | |||||
| * 其包含了多个状态的集合, 其本身自己也是一个状态 | |||||
| * @author hancong03@baidu.com | |||||
| * | |||||
| */ | |||||
| public class MultiState implements State { | |||||
| private boolean state = false; | |||||
| private String info = null; | |||||
| private Map<String, Long> intMap = new HashMap<String, Long>(); | |||||
| private Map<String, String> infoMap = new HashMap<String, String>(); | |||||
| private List<String> stateList = new ArrayList<String>(); | |||||
| public MultiState ( boolean state ) { | |||||
| this.state = state; | |||||
| } | |||||
| public MultiState ( boolean state, String info ) { | |||||
| this.state = state; | |||||
| this.info = info; | |||||
| } | |||||
| public MultiState ( boolean state, int infoKey ) { | |||||
| this.state = state; | |||||
| this.info = AppInfo.getStateInfo( infoKey ); | |||||
| } | |||||
| @Override | |||||
| public boolean isSuccess() { | |||||
| return this.state; | |||||
| } | |||||
| public void addState ( State state ) { | |||||
| stateList.add( state.toJSONString() ); | |||||
| } | |||||
| /** | |||||
| * 该方法调用无效果 | |||||
| */ | |||||
| @Override | |||||
| public void putInfo(String name, String val) { | |||||
| this.infoMap.put(name, val); | |||||
| } | |||||
| @Override | |||||
| public String toJSONString() { | |||||
| String stateVal = this.isSuccess() ? AppInfo.getStateInfo( AppInfo.SUCCESS ) : this.info; | |||||
| StringBuilder builder = new StringBuilder(); | |||||
| builder.append( "{\"state\": \"" + stateVal + "\"" ); | |||||
| // 数字转换 | |||||
| Iterator<String> iterator = this.intMap.keySet().iterator(); | |||||
| while ( iterator.hasNext() ) { | |||||
| stateVal = iterator.next(); | |||||
| builder.append( ",\""+ stateVal +"\": " + this.intMap.get( stateVal ) ); | |||||
| } | |||||
| iterator = this.infoMap.keySet().iterator(); | |||||
| while ( iterator.hasNext() ) { | |||||
| stateVal = iterator.next(); | |||||
| builder.append( ",\""+ stateVal +"\": \"" + this.infoMap.get( stateVal ) + "\"" ); | |||||
| } | |||||
| builder.append( ", list: [" ); | |||||
| iterator = this.stateList.iterator(); | |||||
| while ( iterator.hasNext() ) { | |||||
| builder.append( iterator.next() + "," ); | |||||
| } | |||||
| if ( this.stateList.size() > 0 ) { | |||||
| builder.deleteCharAt( builder.length() - 1 ); | |||||
| } | |||||
| builder.append( " ]}" ); | |||||
| return Encoder.toUnicode( builder.toString() ); | |||||
| } | |||||
| @Override | |||||
| public void putInfo(String name, long val) { | |||||
| this.intMap.put( name, val ); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| package com.iformall.ueditor.define; | |||||
| /** | |||||
| * 处理状态接口 | |||||
| * @author hancong03@baidu.com | |||||
| * | |||||
| */ | |||||
| public interface State { | |||||
| public boolean isSuccess(); | |||||
| public void putInfo(String name, String val); | |||||
| public void putInfo(String name, long val); | |||||
| public String toJSONString(); | |||||
| } | |||||
| @@ -0,0 +1,111 @@ | |||||
| package com.iformall.ueditor.hunter; | |||||
| import com.iformall.ueditor.PathFormat; | |||||
| import com.iformall.ueditor.define.AppInfo; | |||||
| import com.iformall.ueditor.define.BaseState; | |||||
| import com.iformall.ueditor.define.MultiState; | |||||
| import com.iformall.ueditor.define.State; | |||||
| import org.apache.commons.io.FileUtils; | |||||
| import java.io.File; | |||||
| import java.util.Arrays; | |||||
| import java.util.Collection; | |||||
| import java.util.Map; | |||||
| public class FileManager { | |||||
| private String dir = null; | |||||
| private String rootPath = null; | |||||
| private String[] allowFiles = null; | |||||
| private int count = 0; | |||||
| public FileManager(Map<String, Object> conf) { | |||||
| this.rootPath = (String) conf.get("rootPath"); | |||||
| this.dir = this.rootPath + (String) conf.get("dir"); | |||||
| this.allowFiles = this.getAllowFiles(conf.get("allowFiles")); | |||||
| this.count = (Integer) conf.get("count"); | |||||
| } | |||||
| public State listFile(int index) { | |||||
| File dir = new File(this.dir); | |||||
| State state = null; | |||||
| if (!dir.exists()) { | |||||
| return new BaseState(false, AppInfo.NOT_EXIST); | |||||
| } | |||||
| if (!dir.isDirectory()) { | |||||
| return new BaseState(false, AppInfo.NOT_DIRECTORY); | |||||
| } | |||||
| Collection<File> list = FileUtils.listFiles(dir, this.allowFiles, true); | |||||
| if (index < 0 || index > list.size()) { | |||||
| state = new MultiState(true); | |||||
| } else { | |||||
| Object[] fileList = Arrays.copyOfRange(list.toArray(), index, index + this.count); | |||||
| state = this.getState(fileList); | |||||
| } | |||||
| state.putInfo("start", index); | |||||
| state.putInfo("total", list.size()); | |||||
| return state; | |||||
| } | |||||
| private State getState(Object[] files) { | |||||
| MultiState state = new MultiState(true); | |||||
| BaseState fileState = null; | |||||
| File file = null; | |||||
| for (Object obj : files) { | |||||
| if (obj == null) { | |||||
| break; | |||||
| } | |||||
| file = (File) obj; | |||||
| fileState = new BaseState(true); | |||||
| fileState.putInfo("url", PathFormat.format(this.getPath(file))); | |||||
| state.addState(fileState); | |||||
| } | |||||
| return state; | |||||
| } | |||||
| private String getPath(File file) { | |||||
| String path = file.getAbsolutePath(); | |||||
| path = PathFormat.format(path); | |||||
| return path.replace(this.rootPath, "/"); | |||||
| } | |||||
| private String[] getAllowFiles(Object fileExt) { | |||||
| String[] exts = null; | |||||
| String ext = null; | |||||
| if (fileExt == null) { | |||||
| return new String[0]; | |||||
| } | |||||
| exts = (String[]) fileExt; | |||||
| for (int i = 0, len = exts.length; i < len; i++) { | |||||
| ext = exts[i]; | |||||
| exts[i] = ext.replace(".", ""); | |||||
| } | |||||
| return exts; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,140 @@ | |||||
| package com.iformall.ueditor.hunter; | |||||
| import com.iformall.ueditor.PathFormat; | |||||
| import com.iformall.ueditor.define.*; | |||||
| import com.iformall.ueditor.upload.StorageManager; | |||||
| import java.net.HttpURLConnection; | |||||
| import java.net.InetAddress; | |||||
| import java.net.URL; | |||||
| import java.net.UnknownHostException; | |||||
| import java.util.Arrays; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| /** | |||||
| * 图片抓取器 | |||||
| * @author hancong03@baidu.com | |||||
| * | |||||
| */ | |||||
| public class ImageHunter { | |||||
| private String filename = null; | |||||
| private String savePath = null; | |||||
| private String rootPath = null; | |||||
| private List<String> allowTypes = null; | |||||
| private long maxSize = -1; | |||||
| private List<String> filters = null; | |||||
| public ImageHunter ( Map<String, Object> conf ) { | |||||
| this.filename = (String)conf.get( "filename" ); | |||||
| this.savePath = (String)conf.get( "savePath" ); | |||||
| this.rootPath = (String)conf.get( "rootPath" ); | |||||
| this.maxSize = (Long)conf.get( "maxSize" ); | |||||
| this.allowTypes = Arrays.asList( (String[])conf.get( "allowFiles" ) ); | |||||
| this.filters = Arrays.asList( (String[])conf.get( "filter" ) ); | |||||
| } | |||||
| public State capture (String[] list ) { | |||||
| MultiState state = new MultiState( true ); | |||||
| for ( String source : list ) { | |||||
| state.addState( captureRemoteData( source ) ); | |||||
| } | |||||
| return state; | |||||
| } | |||||
| public State captureRemoteData ( String urlStr ) { | |||||
| HttpURLConnection connection = null; | |||||
| URL url = null; | |||||
| String suffix = null; | |||||
| try { | |||||
| url = new URL( urlStr ); | |||||
| if ( !validHost( url.getHost() ) ) { | |||||
| return new BaseState( false, AppInfo.PREVENT_HOST ); | |||||
| } | |||||
| connection = (HttpURLConnection) url.openConnection(); | |||||
| connection.setInstanceFollowRedirects( true ); | |||||
| connection.setUseCaches( true ); | |||||
| if ( !validContentState( connection.getResponseCode() ) ) { | |||||
| return new BaseState( false, AppInfo.CONNECTION_ERROR ); | |||||
| } | |||||
| suffix = MIMEType.getSuffix( connection.getContentType() ); | |||||
| if ( !validFileType( suffix ) ) { | |||||
| return new BaseState( false, AppInfo.NOT_ALLOW_FILE_TYPE ); | |||||
| } | |||||
| if ( !validFileSize( connection.getContentLength() ) ) { | |||||
| return new BaseState( false, AppInfo.MAX_SIZE ); | |||||
| } | |||||
| String savePath = this.getPath( this.savePath, this.filename, suffix ); | |||||
| String physicalPath = this.rootPath + savePath; | |||||
| State state = StorageManager.saveFileByInputStream( connection.getInputStream(), physicalPath ); | |||||
| if ( state.isSuccess() ) { | |||||
| state.putInfo( "url", PathFormat.format( savePath ) ); | |||||
| state.putInfo( "source", urlStr ); | |||||
| } | |||||
| return state; | |||||
| } catch ( Exception e ) { | |||||
| return new BaseState( false, AppInfo.REMOTE_FAIL ); | |||||
| } | |||||
| } | |||||
| private String getPath ( String savePath, String filename, String suffix ) { | |||||
| return PathFormat.parse( savePath + suffix, filename ); | |||||
| } | |||||
| private boolean validHost ( String hostname ) { | |||||
| try { | |||||
| InetAddress ip = InetAddress.getByName(hostname); | |||||
| if (ip.isSiteLocalAddress()) { | |||||
| return false; | |||||
| } | |||||
| } catch (UnknownHostException e) { | |||||
| return false; | |||||
| } | |||||
| return !filters.contains( hostname ); | |||||
| } | |||||
| private boolean validContentState ( int code ) { | |||||
| return HttpURLConnection.HTTP_OK == code; | |||||
| } | |||||
| private boolean validFileType ( String type ) { | |||||
| return this.allowTypes.contains( type ); | |||||
| } | |||||
| private boolean validFileSize ( int size ) { | |||||
| return size < this.maxSize; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,51 @@ | |||||
| package com.iformall.ueditor.upload; | |||||
| import com.iformall.ueditor.PathFormat; | |||||
| import com.iformall.ueditor.define.AppInfo; | |||||
| import com.iformall.ueditor.define.BaseState; | |||||
| import com.iformall.ueditor.define.FileType; | |||||
| import com.iformall.ueditor.define.State; | |||||
| import org.apache.commons.codec.binary.Base64; | |||||
| import java.util.Map; | |||||
| public final class Base64Uploader { | |||||
| public static State save(String content, Map<String, Object> conf) { | |||||
| byte[] data = decode(content); | |||||
| long maxSize = ((Long) conf.get("maxSize")).longValue(); | |||||
| if (!validSize(data, maxSize)) { | |||||
| return new BaseState(false, AppInfo.MAX_SIZE); | |||||
| } | |||||
| String suffix = FileType.getSuffix("JPG"); | |||||
| String savePath = PathFormat.parse((String) conf.get("savePath"), | |||||
| (String) conf.get("filename")); | |||||
| savePath = savePath + suffix; | |||||
| String physicalPath = (String) conf.get("rootPath") + savePath; | |||||
| State storageState = StorageManager.saveBinaryFile(data, physicalPath); | |||||
| if (storageState.isSuccess()) { | |||||
| storageState.putInfo("url", PathFormat.format(savePath)); | |||||
| storageState.putInfo("type", suffix); | |||||
| storageState.putInfo("original", ""); | |||||
| } | |||||
| return storageState; | |||||
| } | |||||
| private static byte[] decode(String content) { | |||||
| return Base64.decodeBase64(content); | |||||
| } | |||||
| private static boolean validSize(byte[] data, long length) { | |||||
| return data.length <= length; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,70 @@ | |||||
| package com.iformall.ueditor.upload; | |||||
| import com.iformall.ueditor.PathFormat; | |||||
| import com.iformall.ueditor.define.AppInfo; | |||||
| import com.iformall.ueditor.define.BaseState; | |||||
| import com.iformall.ueditor.define.FileType; | |||||
| import com.iformall.ueditor.define.State; | |||||
| import org.apache.commons.fileupload.servlet.ServletFileUpload; | |||||
| import org.springframework.web.multipart.MultipartFile; | |||||
| import org.springframework.web.multipart.MultipartHttpServletRequest; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStream; | |||||
| import java.util.Arrays; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| public class BinaryUploader { | |||||
| public static final State save(HttpServletRequest request, Map<String, Object> conf) { | |||||
| if (!ServletFileUpload.isMultipartContent(request)) { | |||||
| return new BaseState(false, AppInfo.NOT_MULTIPART_CONTENT); | |||||
| } | |||||
| MultipartFile file = ((MultipartHttpServletRequest) request).getFile("upfile"); | |||||
| try { | |||||
| if (file == null) { | |||||
| return new BaseState(false, AppInfo.NOTFOUND_UPLOAD_DATA); | |||||
| } | |||||
| String savePath = (String) conf.get("savePath"); | |||||
| String originFileName = file.getOriginalFilename(); | |||||
| String suffix = FileType.getSuffixByFilename(file.getOriginalFilename()); | |||||
| originFileName = originFileName.substring(0, | |||||
| originFileName.length() - suffix.length()); | |||||
| savePath = savePath + suffix; | |||||
| long maxSize = ((Long) conf.get("maxSize")).longValue(); | |||||
| if (!validType(suffix, (String[]) conf.get("allowFiles"))) { | |||||
| return new BaseState(false, AppInfo.NOT_ALLOW_FILE_TYPE); | |||||
| } | |||||
| savePath = PathFormat.parse(savePath, originFileName); | |||||
| String physicalPath = (String) conf.get("rootPath") + savePath; | |||||
| InputStream is = file.getInputStream(); | |||||
| State storageState = StorageManager.saveFileByInputStream(is, physicalPath, maxSize); | |||||
| is.close(); | |||||
| if (storageState.isSuccess()) { | |||||
| storageState.putInfo("url", PathFormat.format(savePath)); | |||||
| storageState.putInfo("local", physicalPath); | |||||
| storageState.putInfo("type", suffix); | |||||
| storageState.putInfo("original", originFileName + suffix); | |||||
| } | |||||
| return storageState; | |||||
| } catch (IOException e) { | |||||
| } | |||||
| return new BaseState(false, AppInfo.IO_ERROR); | |||||
| } | |||||
| private static boolean validType(String type, String[] allowTypes) { | |||||
| List<String> list = Arrays.asList(allowTypes); | |||||
| return list.contains(type); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,148 @@ | |||||
| package com.iformall.ueditor.upload; | |||||
| import com.iformall.ueditor.define.AppInfo; | |||||
| import com.iformall.ueditor.define.BaseState; | |||||
| import com.iformall.ueditor.define.State; | |||||
| import org.apache.commons.io.FileUtils; | |||||
| import java.io.*; | |||||
| public class StorageManager { | |||||
| public static final int BUFFER_SIZE = 8192; | |||||
| public StorageManager() { | |||||
| } | |||||
| public static State saveBinaryFile(byte[] data, String path) { | |||||
| File file = new File(path); | |||||
| State state = valid(file); | |||||
| if (!state.isSuccess()) { | |||||
| return state; | |||||
| } | |||||
| try { | |||||
| BufferedOutputStream bos = new BufferedOutputStream( | |||||
| new FileOutputStream(file)); | |||||
| bos.write(data); | |||||
| bos.flush(); | |||||
| bos.close(); | |||||
| } catch (IOException ioe) { | |||||
| return new BaseState(false, AppInfo.IO_ERROR); | |||||
| } | |||||
| state = new BaseState(true, file.getAbsolutePath()); | |||||
| state.putInfo("size", data.length); | |||||
| state.putInfo("title", file.getName()); | |||||
| return state; | |||||
| } | |||||
| public static State saveFileByInputStream(InputStream is, String path, long maxSize) { | |||||
| State state = null; | |||||
| File tmpFile = getTmpFile(); | |||||
| byte[] dataBuf = new byte[2048]; | |||||
| BufferedInputStream bis = new BufferedInputStream(is, StorageManager.BUFFER_SIZE); | |||||
| try { | |||||
| BufferedOutputStream bos = new BufferedOutputStream( | |||||
| new FileOutputStream(tmpFile), StorageManager.BUFFER_SIZE); | |||||
| int count = 0; | |||||
| while ((count = bis.read(dataBuf)) != -1) { | |||||
| bos.write(dataBuf, 0, count); | |||||
| } | |||||
| bos.flush(); | |||||
| bos.close(); | |||||
| if (tmpFile.length() > maxSize) { | |||||
| tmpFile.delete(); | |||||
| return new BaseState(false, AppInfo.MAX_SIZE); | |||||
| } | |||||
| state = saveTmpFile(tmpFile, path); | |||||
| if (!state.isSuccess()) { | |||||
| tmpFile.delete(); | |||||
| } | |||||
| return state; | |||||
| } catch (IOException e) { | |||||
| } | |||||
| return new BaseState(false, AppInfo.IO_ERROR); | |||||
| } | |||||
| public static State saveFileByInputStream(InputStream is, String path) { | |||||
| State state = null; | |||||
| File tmpFile = getTmpFile(); | |||||
| byte[] dataBuf = new byte[2048]; | |||||
| BufferedInputStream bis = new BufferedInputStream(is, StorageManager.BUFFER_SIZE); | |||||
| try { | |||||
| BufferedOutputStream bos = new BufferedOutputStream( | |||||
| new FileOutputStream(tmpFile), StorageManager.BUFFER_SIZE); | |||||
| int count = 0; | |||||
| while ((count = bis.read(dataBuf)) != -1) { | |||||
| bos.write(dataBuf, 0, count); | |||||
| } | |||||
| bos.flush(); | |||||
| bos.close(); | |||||
| state = saveTmpFile(tmpFile, path); | |||||
| if (!state.isSuccess()) { | |||||
| tmpFile.delete(); | |||||
| } | |||||
| return state; | |||||
| } catch (IOException e) { | |||||
| } | |||||
| return new BaseState(false, AppInfo.IO_ERROR); | |||||
| } | |||||
| private static File getTmpFile() { | |||||
| File tmpDir = FileUtils.getTempDirectory(); | |||||
| String tmpFileName = (Math.random() * 10000 + "").replace(".", ""); | |||||
| return new File(tmpDir, tmpFileName); | |||||
| } | |||||
| private static State saveTmpFile(File tmpFile, String path) { | |||||
| State state = null; | |||||
| File targetFile = new File(path); | |||||
| if (targetFile.canWrite()) { | |||||
| return new BaseState(false, AppInfo.PERMISSION_DENIED); | |||||
| } | |||||
| try { | |||||
| FileUtils.moveFile(tmpFile, targetFile); | |||||
| } catch (IOException e) { | |||||
| return new BaseState(false, AppInfo.IO_ERROR); | |||||
| } | |||||
| state = new BaseState(true); | |||||
| state.putInfo("size", targetFile.length()); | |||||
| state.putInfo("title", targetFile.getName()); | |||||
| return state; | |||||
| } | |||||
| private static State valid(File file) { | |||||
| File parentPath = file.getParentFile(); | |||||
| if ((!parentPath.exists()) && (!parentPath.mkdirs())) { | |||||
| return new BaseState(false, AppInfo.FAILED_CREATE_FILE); | |||||
| } | |||||
| if (!parentPath.canWrite()) { | |||||
| return new BaseState(false, AppInfo.PERMISSION_DENIED); | |||||
| } | |||||
| return new BaseState(true); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,110 @@ | |||||
| package com.iformall.ueditor.upload; | |||||
| import com.alibaba.fastjson.JSON; | |||||
| import com.alibaba.fastjson.JSONObject; | |||||
| import com.amazonaws.AmazonClientException; | |||||
| import com.amazonaws.AmazonServiceException; | |||||
| import com.amazonaws.auth.AWSCredentials; | |||||
| import com.amazonaws.auth.AWSCredentialsProvider; | |||||
| import com.amazonaws.auth.BasicAWSCredentials; | |||||
| import com.amazonaws.services.s3.AmazonS3; | |||||
| import com.amazonaws.services.s3.AmazonS3ClientBuilder; | |||||
| import com.amazonaws.services.s3.model.CannedAccessControlList; | |||||
| import com.amazonaws.services.s3.model.ObjectMetadata; | |||||
| import com.amazonaws.services.s3.model.PutObjectRequest; | |||||
| import com.iformall.ueditor.define.State; | |||||
| import org.springframework.web.multipart.MultipartFile; | |||||
| import org.springframework.web.multipart.MultipartHttpServletRequest; | |||||
| import javax.servlet.http.HttpServletRequest; | |||||
| import java.io.IOException; | |||||
| import java.net.URL; | |||||
| import java.util.Map; | |||||
| import java.util.UUID; | |||||
| public class Uploader { | |||||
| private HttpServletRequest request = null; | |||||
| private Map<String, Object> conf = null; | |||||
| public Uploader(HttpServletRequest request, Map<String, Object> conf) { | |||||
| this.request = request; | |||||
| this.conf = conf; | |||||
| } | |||||
| public final State doExec() { | |||||
| String filedName = (String) this.conf.get("fieldName"); | |||||
| State state = null; | |||||
| if ("true".equals(this.conf.get("isBase64"))) { | |||||
| state = Base64Uploader.save(this.request.getParameter(filedName), | |||||
| this.conf); | |||||
| } else { | |||||
| state = new BinaryUploader().save(this.request, this.conf); | |||||
| if (state.isSuccess()) { | |||||
| JSONObject stateJson = JSON.parseObject(state.toJSONString()); | |||||
| MultipartFile multiReq= ((MultipartHttpServletRequest) this.request).getFile("upfile"); | |||||
| String tenantId = "789"; //(String)SecurityUtils.getSubject().getSession().getAttribute(UserSession.tenantId); | |||||
| ObjectMetadata metadata = new ObjectMetadata(); | |||||
| metadata.setContentType(multiReq.getContentType()); | |||||
| metadata.setContentLength(multiReq.getSize()); | |||||
| try { | |||||
| AmazonS3 s3 = AmazonS3ClientBuilder.standard() | |||||
| .withRegion(conf.get("clientRegion").toString()) | |||||
| .withCredentials(new AWSCredentialsProvider() { | |||||
| @Override | |||||
| public AWSCredentials getCredentials() { | |||||
| return new BasicAWSCredentials(conf.get("access").toString(), conf.get("secret").toString()); | |||||
| } | |||||
| @Override | |||||
| public void refresh() { | |||||
| } | |||||
| }) | |||||
| .build(); | |||||
| String fileName = UUID.randomUUID().toString(); | |||||
| int dot = multiReq.getOriginalFilename().lastIndexOf('.'); | |||||
| if (dot >= 0) { | |||||
| fileName = tenantId + "/" + fileName + multiReq.getOriginalFilename().substring(dot, multiReq.getOriginalFilename().length()); | |||||
| } else { | |||||
| fileName = tenantId + "/" + fileName; | |||||
| } | |||||
| s3.putObject( | |||||
| new PutObjectRequest(conf.get("bucketName").toString(), fileName, multiReq.getInputStream(), metadata) | |||||
| .withCannedAcl(CannedAccessControlList.PublicRead)); | |||||
| URL url = s3.getUrl(conf.get("bucketName").toString(), fileName); | |||||
| //logger.info(url.toString()); | |||||
| state.putInfo("url", url.toString()); | |||||
| } catch (AmazonServiceException ase) { | |||||
| /* | |||||
| logger.warn("Caught an AmazonServiceException, which " + | |||||
| "means your request made it " + | |||||
| "to Amazon S3, but was rejected with an error response" + | |||||
| " for some reason."); | |||||
| logger.warn(ase.getMessage()); | |||||
| */ | |||||
| } catch (AmazonClientException ace) { | |||||
| /* | |||||
| logger.warn("Caught an AmazonClientException, which " + | |||||
| "means the client encountered " + | |||||
| "an internal error while trying to " + | |||||
| "communicate with S3, " + | |||||
| "such as not being able to access the network."); | |||||
| logger.warn("Error Message: " + ace.getMessage()); | |||||
| */ | |||||
| } catch (IOException ioe) { | |||||
| //logger.warn("Caught an IOException: " + ioe.getMessage()); | |||||
| } | |||||
| } | |||||
| } | |||||
| return state; | |||||
| } | |||||
| } | |||||
| @@ -67,6 +67,12 @@ aws: | |||||
| access: ENC(3gx5ghDFBqGrEhO3Wf8aYmXsnwHO7Cj3HNKJGOeUj0o=) | access: ENC(3gx5ghDFBqGrEhO3Wf8aYmXsnwHO7Cj3HNKJGOeUj0o=) | ||||
| secret: ENC(HVKIJwCJKVXLlUpGlQPwNqJOlnpxn4xYuy91SH0seTSm2uAttIQHvA49fXWWax90v5wloIk0QuU=) | secret: ENC(HVKIJwCJKVXLlUpGlQPwNqJOlnpxn4xYuy91SH0seTSm2uAttIQHvA49fXWWax90v5wloIk0QuU=) | ||||
| ueditor: | |||||
| config: config.json | |||||
| unified: true | |||||
| upload-path: ./upload/ | |||||
| url-prefix: http://localhost:9000/upload/ | |||||
| wechat: | wechat: | ||||
| open: | open: | ||||
| componentAppId: "wx897e4673286c915d" | componentAppId: "wx897e4673286c915d" | ||||