Kaynağa Gözat

手工整理#243

zhourui 5 yıl önce
ebeveyn
işleme
e495ca597e
19 değiştirilmiş dosya ile 1133 ekleme ve 222 silme
  1. 231 4
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/Applications.java
  2. 13 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/ExceptionFindApplicationName.java
  3. 9 9
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Collect.java
  4. 85 6
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/CipherConnectionAction.java
  5. 236 145
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/ConnectionAction.java
  6. 21 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/ExceptionBinary.java
  7. 22 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/ExceptionDeleteBinary.java
  8. 22 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/ExceptionGetBinary.java
  9. 22 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/ExceptionMultiPartBinary.java
  10. 26 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/ExceptionReadBinary.java
  11. 57 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/FilePart.java
  12. 34 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/FormField.java
  13. 6 0
      o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/StringTools.java
  14. 92 14
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/ActionDocToWordWorkOrWorkCompleted.java
  15. 59 11
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/ActionDownloadWithWork.java
  16. 62 12
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/ActionDownloadWithWorkCompleted.java
  17. 62 12
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/ActionDownloadWithWorkCompletedStream.java
  18. 61 9
      o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/ActionDownloadWithWorkStream.java
  19. 13 0
      o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/ExceptionFromMappingNotExist.java

+ 231 - 4
o2server/x_base_core_project/src/main/java/com/x/base/core/project/Applications.java

@@ -2,6 +2,7 @@ package com.x.base.core.project;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
+import java.util.Collection;
 import java.util.Date;
 import java.util.List;
 import java.util.Random;
@@ -16,6 +17,8 @@ import java.util.zip.CRC32;
 import com.x.base.core.project.config.Config;
 import com.x.base.core.project.connection.ActionResponse;
 import com.x.base.core.project.connection.CipherConnectionAction;
+import com.x.base.core.project.connection.FilePart;
+import com.x.base.core.project.connection.FormField;
 import com.x.base.core.project.connection.HttpConnection;
 import com.x.base.core.project.logger.Logger;
 import com.x.base.core.project.logger.LoggerFactory;
@@ -108,7 +111,7 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 			throws Exception {
 		String name = this.findApplicationName(applicationName);
 		if (StringUtils.isEmpty(name)) {
-			throw new Exception("getQuery can not find application with name:" + applicationName + ".");
+			throw new ExceptionFindApplicationName(applicationName);
 		}
 		Application application = null;
 		if (StringUtils.isEmpty(seed)) {
@@ -123,6 +126,59 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 		return CipherConnectionAction.get(xdebugger, application.getUrlJaxrsRoot() + CipherConnectionAction.trim(uri));
 	}
 
+	public byte[] getQueryBinary(Class<?> applicationClass, String uri) throws Exception {
+		return this.getQueryBinary(false, applicationClass.getName(), uri, null);
+	}
+
+	public byte[] getQueryBinary(Class<?> applicationClass, String uri, String seed) throws Exception {
+		return this.getQueryBinary(false, applicationClass.getName(), uri, seed);
+	}
+
+	public byte[] getQueryBinary(Boolean xdebugger, Class<?> applicationClass, String uri) throws Exception {
+		return this.getQueryBinary(xdebugger, applicationClass.getName(), uri, null);
+	}
+
+	public byte[] getQueryBinary(Boolean xdebugger, Class<?> applicationClass, String uri, String seed)
+			throws Exception {
+		return this.getQueryBinary(xdebugger, applicationClass.getName(), uri, seed);
+	}
+
+	public byte[] getQueryBinary(Application application, String uri) throws Exception {
+		return this.getQueryBinary(false, application, uri);
+	}
+
+	public byte[] getQueryBinary(Boolean xdebugger, Application application, String uri) throws Exception {
+		return CipherConnectionAction.getBinary(xdebugger,
+				application.getUrlJaxrsRoot() + CipherConnectionAction.trim(uri));
+	}
+
+	public byte[] getQueryBinary(String applicationName, String uri) throws Exception {
+		return getQueryBinary(false, applicationName, uri, null);
+	}
+
+	public byte[] getQueryBinary(String applicationName, String uri, String seed) throws Exception {
+		return getQueryBinary(false, applicationName, uri, seed);
+	}
+
+	public byte[] getQueryBinary(Boolean xdebugger, String applicationName, String uri, String seed) throws Exception {
+		String name = this.findApplicationName(applicationName);
+		if (StringUtils.isEmpty(name)) {
+			throw new ExceptionFindApplicationName(applicationName);
+		}
+		Application application = null;
+		if (StringUtils.isEmpty(seed)) {
+			// 如果随机种子是空,那么优先使用本机
+			application = this.findApplicationWithNode(name, Config.node());
+			if (null == application) {
+				application = this.randomWithWeight(name);
+			}
+		} else {
+			application = this.randomWithSeed(name, seed);
+		}
+		return CipherConnectionAction.getBinary(xdebugger,
+				application.getUrlJaxrsRoot() + CipherConnectionAction.trim(uri));
+	}
+
 	public ActionResponse deleteQuery(Class<?> applicationClass, String uri) throws Exception {
 		return this.deleteQuery(false, applicationClass.getName(), uri, null);
 	}
@@ -161,7 +217,7 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 			throws Exception {
 		String name = this.findApplicationName(applicationName);
 		if (StringUtils.isEmpty(name)) {
-			throw new Exception("deleteQuery can not find application with name:" + applicationName + ".");
+			throw new ExceptionFindApplicationName(applicationName);
 		}
 		Application application = null;
 		if (StringUtils.isEmpty(seed)) {
@@ -177,6 +233,60 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 				application.getUrlJaxrsRoot() + CipherConnectionAction.trim(uri));
 	}
 
+	public byte[] deleteQueryBinary(Class<?> applicationClass, String uri) throws Exception {
+		return this.deleteQueryBinary(false, applicationClass.getName(), uri, null);
+	}
+
+	public byte[] deleteQueryBinary(Class<?> applicationClass, String uri, String seed) throws Exception {
+		return this.deleteQueryBinary(false, applicationClass.getName(), uri, seed);
+	}
+
+	public byte[] deleteQueryBinary(Boolean xdebugger, Class<?> applicationClass, String uri) throws Exception {
+		return this.deleteQueryBinary(xdebugger, applicationClass.getName(), uri, null);
+	}
+
+	public byte[] deleteQueryBinary(Boolean xdebugger, Class<?> applicationClass, String uri, String seed)
+			throws Exception {
+		return this.deleteQueryBinary(xdebugger, applicationClass.getName(), uri, seed);
+	}
+
+	public byte[] deleteQueryBinary(Application application, String uri) throws Exception {
+		return this.deleteQueryBinary(false, application, uri);
+	}
+
+	public byte[] deleteQueryBinary(Boolean xdebugger, Application application, String uri) throws Exception {
+		return CipherConnectionAction.deleteBinary(xdebugger,
+				application.getUrlJaxrsRoot() + CipherConnectionAction.trim(uri));
+	}
+
+	public byte[] deleteQueryBinary(String applicationName, String uri) throws Exception {
+		return deleteQueryBinary(false, applicationName, uri, null);
+	}
+
+	public byte[] deleteQueryBinary(String applicationName, String uri, String seed) throws Exception {
+		return deleteQueryBinary(false, applicationName, uri, seed);
+	}
+
+	public byte[] deleteQueryBinary(Boolean xdebugger, String applicationName, String uri, String seed)
+			throws Exception {
+		String name = this.findApplicationName(applicationName);
+		if (StringUtils.isEmpty(name)) {
+			throw new ExceptionFindApplicationName(applicationName);
+		}
+		Application application = null;
+		if (StringUtils.isEmpty(seed)) {
+			// 如果随机种子是空,那么优先使用本机
+			application = this.findApplicationWithNode(name, Config.node());
+			if (null == application) {
+				application = this.randomWithWeight(name);
+			}
+		} else {
+			application = this.randomWithSeed(name, seed);
+		}
+		return CipherConnectionAction.deleteBinary(xdebugger,
+				application.getUrlJaxrsRoot() + CipherConnectionAction.trim(uri));
+	}
+
 	public ActionResponse postQuery(Class<?> applicationClass, String uri, Object body) throws Exception {
 		return this.postQuery(false, applicationClass.getName(), uri, body, null);
 	}
@@ -217,7 +327,7 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 			throws Exception {
 		String name = this.findApplicationName(applicationName);
 		if (StringUtils.isEmpty(name)) {
-			throw new Exception("postQuery can not find application with name:" + applicationName + ".");
+			throw new ExceptionFindApplicationName(applicationName);
 		}
 		Application application = null;
 		if (StringUtils.isEmpty(seed)) {
@@ -233,6 +343,123 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 				body);
 	}
 
+	public byte[] postQueryBinary(Class<?> applicationClass, String uri, Object body) throws Exception {
+		return this.postQueryBinary(false, applicationClass.getName(), uri, body, null);
+	}
+
+	public byte[] postQueryBinary(Class<?> applicationClass, String uri, Object body, String seed) throws Exception {
+		return this.postQueryBinary(false, applicationClass.getName(), uri, body, seed);
+	}
+
+	public byte[] postQueryBinary(Boolean xdebugger, Class<?> applicationClass, String uri, Object body)
+			throws Exception {
+		return this.postQueryBinary(xdebugger, applicationClass.getName(), uri, body, null);
+	}
+
+	public byte[] postQueryBinary(Boolean xdebugger, Class<?> applicationClass, String uri, Object body, String seed)
+			throws Exception {
+		return this.postQueryBinary(xdebugger, applicationClass.getName(), uri, body, seed);
+	}
+
+	public byte[] postQueryBinary(Application application, String uri, Object body) throws Exception {
+		return this.postQueryBinary(false, application, uri, body);
+	}
+
+	public byte[] postQueryBinary(Boolean xdebugger, Application application, String uri, Object body)
+			throws Exception {
+		return CipherConnectionAction.postBinary(xdebugger,
+				application.getUrlJaxrsRoot() + CipherConnectionAction.trim(uri), body);
+	}
+
+	public byte[] postQueryBinary(String applicationName, String uri, Object body) throws Exception {
+		return this.postQueryBinary(false, applicationName, uri, body, null);
+	}
+
+	public byte[] postQueryBinary(String applicationName, String uri, Object body, String seed) throws Exception {
+		return this.postQueryBinary(false, applicationName, uri, body, seed);
+	}
+
+	public byte[] postQueryBinary(Boolean xdebugger, String applicationName, String uri, Object body, String seed)
+			throws Exception {
+		String name = this.findApplicationName(applicationName);
+		if (StringUtils.isEmpty(name)) {
+			throw new ExceptionFindApplicationName(applicationName);
+		}
+		Application application = null;
+		if (StringUtils.isEmpty(seed)) {
+			// 如果随机种子是空,那么优先使用本机
+			application = this.findApplicationWithNode(name, Config.node());
+			if (null == application) {
+				application = this.randomWithWeight(name);
+			}
+		} else {
+			application = this.randomWithSeed(name, seed);
+		}
+		return CipherConnectionAction.postBinary(xdebugger,
+				application.getUrlJaxrsRoot() + CipherConnectionAction.trim(uri), body);
+	}
+
+	public byte[] postQueryMultiPartinary(Class<?> applicationClass, String uri, Collection<FormField> formFields,
+			Collection<FilePart> fileParts) throws Exception {
+		return this.postQueryMultiPartBinary(false, applicationClass.getName(), uri, formFields, fileParts, null);
+	}
+
+	public byte[] postQueryMultiPartBinary(Class<?> applicationClass, String uri, Collection<FormField> formFields,
+			Collection<FilePart> fileParts, String seed) throws Exception {
+		return this.postQueryMultiPartBinary(false, applicationClass.getName(), uri, formFields, fileParts, seed);
+	}
+
+	public byte[] postQueryMultiPartBinary(Boolean xdebugger, Class<?> applicationClass, String uri,
+			Collection<FormField> formFields, Collection<FilePart> fileParts) throws Exception {
+		return this.postQueryMultiPartBinary(xdebugger, applicationClass.getName(), uri, formFields, fileParts, null);
+	}
+
+	public byte[] postQueryMultiPartBinary(Boolean xdebugger, Class<?> applicationClass, String uri,
+			Collection<FormField> formFields, Collection<FilePart> fileParts, String seed) throws Exception {
+		return this.postQueryMultiPartBinary(xdebugger, applicationClass.getName(), uri, formFields, fileParts, seed);
+	}
+
+	public byte[] postQueryMultiPartBinary(Application application, String uri, Collection<FormField> formFields,
+			Collection<FilePart> fileParts) throws Exception {
+		return this.postQueryMultiPartBinary(false, application, uri, formFields, fileParts);
+	}
+
+	public byte[] postQueryMultiPartBinary(Boolean xdebugger, Application application, String uri,
+			Collection<FormField> formFields, Collection<FilePart> fileParts) throws Exception {
+		return CipherConnectionAction.postMultiPartBinary(xdebugger,
+				application.getUrlJaxrsRoot() + CipherConnectionAction.trim(uri), formFields, fileParts);
+	}
+
+	public byte[] postQueryMultiPartBinary(String applicationName, String uri, Collection<FormField> formFields,
+			Collection<FilePart> fileParts) throws Exception {
+		return this.postQueryMultiPartBinary(false, applicationName, uri, formFields, fileParts, null);
+	}
+
+	public byte[] postQueryMultiPartBinary(String applicationName, String uri, Collection<FormField> formFields,
+			Collection<FilePart> fileParts, String seed) throws Exception {
+		return this.postQueryMultiPartBinary(false, applicationName, uri, formFields, fileParts, seed);
+	}
+
+	public byte[] postQueryMultiPartBinary(Boolean xdebugger, String applicationName, String uri,
+			Collection<FormField> formFields, Collection<FilePart> fileParts, String seed) throws Exception {
+		String name = this.findApplicationName(applicationName);
+		if (StringUtils.isEmpty(name)) {
+			throw new ExceptionFindApplicationName(applicationName);
+		}
+		Application application = null;
+		if (StringUtils.isEmpty(seed)) {
+			// 如果随机种子是空,那么优先使用本机
+			application = this.findApplicationWithNode(name, Config.node());
+			if (null == application) {
+				application = this.randomWithWeight(name);
+			}
+		} else {
+			application = this.randomWithSeed(name, seed);
+		}
+		return CipherConnectionAction.postMultiPartBinary(xdebugger,
+				application.getUrlJaxrsRoot() + CipherConnectionAction.trim(uri), formFields, fileParts);
+	}
+
 	public ActionResponse putQuery(Class<?> applicationClass, String uri, Object body) throws Exception {
 		return this.putQuery(false, applicationClass.getName(), uri, body, null);
 	}
@@ -273,7 +500,7 @@ public class Applications extends ConcurrentHashMap<String, CopyOnWriteArrayList
 			throws Exception {
 		String name = this.findApplicationName(applicationName);
 		if (StringUtils.isEmpty(name)) {
-			throw new Exception("putQuery can not find application with name:" + applicationName + ".");
+			throw new ExceptionFindApplicationName(applicationName);
 		}
 		Application application = null;
 		if (StringUtils.isEmpty(seed)) {

+ 13 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/ExceptionFindApplicationName.java

@@ -0,0 +1,13 @@
+package com.x.base.core.project;
+
+import com.x.base.core.project.exception.PromptException;
+
+public class ExceptionFindApplicationName extends PromptException {
+
+	private static final long serialVersionUID = -7354813827434276962L;
+
+	public ExceptionFindApplicationName(String name) {
+		super("can not find application with name:{}.", name);
+	}
+
+}

+ 9 - 9
o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Collect.java

@@ -173,14 +173,14 @@ public class Collect extends ConfigObject {
 		try {
 			URL url = new URL(this.url("/o2_collect_assemble/jaxrs/collect/validate"));
 			HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-			connection.setRequestProperty(ConnectionAction.Access_Control_Allow_Credentials,
-					ConnectionAction.Access_Control_Allow_Credentials_Value);
-			connection.setRequestProperty(ConnectionAction.Access_Control_Allow_Headers,
-					ConnectionAction.Access_Control_Allow_Headers_Value);
-			connection.setRequestProperty(ConnectionAction.Access_Control_Allow_Methods,
-					ConnectionAction.Access_Control_Allow_Methods_Value);
-			connection.setRequestProperty(ConnectionAction.Cache_Control, ConnectionAction.Cache_Control_Value);
-			connection.setRequestProperty(ConnectionAction.Content_Type, ConnectionAction.Content_Type_Value);
+			connection.setRequestProperty(ConnectionAction.ACCESS_CONTROL_ALLOW_CREDENTIALS,
+					ConnectionAction.ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE);
+			connection.setRequestProperty(ConnectionAction.ACCESS_CONTROL_ALLOW_HEADERS,
+					ConnectionAction.ACCESS_CONTROL_ALLOW_HEADERS_VALUE);
+			connection.setRequestProperty(ConnectionAction.ACCESS_CONTROL_ALLOW_METHODS,
+					ConnectionAction.ACCESS_CONTROL_ALLOW_METHODS_VALUE);
+			connection.setRequestProperty(ConnectionAction.CACHE_CONTROL, ConnectionAction.CACHE_CONTROL_VALUE);
+			connection.setRequestProperty(ConnectionAction.CONTENT_TYPE, ConnectionAction.CONTENT_TYPE_VALUE);
 			connection.setRequestMethod("POST");
 			connection.setUseCaches(false);
 			connection.setDoOutput(true);
@@ -208,7 +208,7 @@ public class Collect extends ConfigObject {
 		return true;
 	}
 
-	public boolean connect() throws Exception{
+	public boolean connect() throws Exception {
 		if (!Config.collect().getEnable()) {
 			throw new ExceptionCollectDisable();
 		}

+ 85 - 6
o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/CipherConnectionAction.java

@@ -1,10 +1,7 @@
 package com.x.base.core.project.connection;
 
+import java.util.Collection;
 import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.lang3.BooleanUtils;
-import org.apache.commons.lang3.StringUtils;
 
 import com.x.base.core.project.Application;
 import com.x.base.core.project.Applications;
@@ -14,8 +11,14 @@ import com.x.base.core.project.http.EffectivePerson;
 import com.x.base.core.project.http.HttpToken;
 import com.x.base.core.project.tools.ListTools;
 
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+
 public class CipherConnectionAction {
 
+	private CipherConnectionAction() {
+	}
+
 	public static ActionResponse get(Boolean xdebugger, String address) throws Exception {
 		List<NameValuePair> headers = cipher();
 		if (BooleanUtils.isTrue(xdebugger)) {
@@ -29,6 +32,19 @@ public class CipherConnectionAction {
 		return get(xdebugger, addr);
 	}
 
+	public static byte[] getBinary(Boolean xdebugger, String address) throws Exception {
+		List<NameValuePair> headers = cipher();
+		if (BooleanUtils.isTrue(xdebugger)) {
+			headers.add(new NameValuePair(HttpToken.X_Debugger, true));
+		}
+		return ConnectionAction.getBinary(address, headers);
+	}
+
+	public static byte[] getBinary(Boolean xdebugger, Application application, String... strs) throws Exception {
+		String addr = application.getUrlJaxrsRoot() + trim(Applications.joinQueryUri(strs));
+		return getBinary(xdebugger, addr);
+	}
+
 	public static ActionResponse delete(Boolean xdebugger, String address) throws Exception {
 		List<NameValuePair> headers = cipher();
 		if (BooleanUtils.isTrue(xdebugger)) {
@@ -42,6 +58,19 @@ public class CipherConnectionAction {
 		return delete(xdebugger, addr);
 	}
 
+	public static byte[] deleteBinary(Boolean xdebugger, String address) throws Exception {
+		List<NameValuePair> headers = cipher();
+		if (BooleanUtils.isTrue(xdebugger)) {
+			headers.add(new NameValuePair(HttpToken.X_Debugger, true));
+		}
+		return ConnectionAction.deleteBinary(address, headers);
+	}
+
+	public static byte[] deleteBinary(Boolean xdebugger, Application application, String... strs) throws Exception {
+		String addr = application.getUrlJaxrsRoot() + trim(Applications.joinQueryUri(strs));
+		return deleteBinary(xdebugger, addr);
+	}
+
 	public static ActionResponse post(Boolean xdebugger, String address, Object body) throws Exception {
 		List<NameValuePair> headers = cipher();
 		if (BooleanUtils.isTrue(xdebugger)) {
@@ -56,6 +85,35 @@ public class CipherConnectionAction {
 		return post(xdebugger, addr, body);
 	}
 
+	public static byte[] postBinary(Boolean xdebugger, String address, Object body) throws Exception {
+		List<NameValuePair> headers = cipher();
+		if (BooleanUtils.isTrue(xdebugger)) {
+			headers.add(new NameValuePair(HttpToken.X_Debugger, true));
+		}
+		return ConnectionAction.postBinary(address, headers, body);
+	}
+
+	public static byte[] postBinary(Boolean xdebugger, Object body, Application application, String... strs)
+			throws Exception {
+		String addr = application.getUrlJaxrsRoot() + trim(Applications.joinQueryUri(strs));
+		return postBinary(xdebugger, addr, body);
+	}
+
+	public static byte[] postMultiPartBinary(Boolean xdebugger, String address, Collection<FormField> formFields,
+			Collection<FilePart> fileParts) throws Exception {
+		List<NameValuePair> headers = cipher();
+		if (BooleanUtils.isTrue(xdebugger)) {
+			headers.add(new NameValuePair(HttpToken.X_Debugger, true));
+		}
+		return ConnectionAction.postMultiPartBinary(address, headers, formFields, fileParts);
+	}
+
+	public static byte[] postMultiPartBinary(Boolean xdebugger, Collection<FormField> formFields,
+			Collection<FilePart> fileParts, Application application, String... strs) throws Exception {
+		String addr = application.getUrlJaxrsRoot() + trim(Applications.joinQueryUri(strs));
+		return postMultiPartBinary(xdebugger, addr, formFields, fileParts);
+	}
+
 	public static ActionResponse put(Boolean xdebugger, String address, Object body) throws Exception {
 		List<NameValuePair> headers = cipher();
 		if (BooleanUtils.isTrue(xdebugger)) {
@@ -70,12 +128,33 @@ public class CipherConnectionAction {
 		return put(xdebugger, addr, body);
 	}
 
-	public static ActionResponse multiFormPost(Boolean xdebugger, String address, String fileName, byte[] bytes, Map<String, String> map) throws Exception{
+	public static byte[] putBinary(Boolean xdebugger, String address, Object body) throws Exception {
+		List<NameValuePair> headers = cipher();
+		if (BooleanUtils.isTrue(xdebugger)) {
+			headers.add(new NameValuePair(HttpToken.X_Debugger, true));
+		}
+		return ConnectionAction.putBinary(address, headers, body);
+	}
+
+	public static byte[] putBinary(Boolean xdebugger, Object body, Application application, String... strs)
+			throws Exception {
+		String addr = application.getUrlJaxrsRoot() + trim(Applications.joinQueryUri(strs));
+		return putBinary(xdebugger, addr, body);
+	}
+
+	public static byte[] putMultiPartBinary(Boolean xdebugger, String address, Collection<FormField> formFields,
+			Collection<FilePart> fileParts) throws Exception {
 		List<NameValuePair> headers = cipher();
 		if (BooleanUtils.isTrue(xdebugger)) {
 			headers.add(new NameValuePair(HttpToken.X_Debugger, true));
 		}
-		return ConnectionAction.multiFormPost(address, headers, fileName, bytes, map);
+		return ConnectionAction.putMultiPartBinary(address, headers, formFields, fileParts);
+	}
+
+	public static byte[] putMultiPartBinary(Boolean xdebugger, Collection<FormField> formFields,
+			Collection<FilePart> fileParts, Application application, String... strs) throws Exception {
+		String addr = application.getUrlJaxrsRoot() + trim(Applications.joinQueryUri(strs));
+		return putMultiPartBinary(xdebugger, addr, formFields, fileParts);
 	}
 
 	public static List<NameValuePair> cipher() throws Exception {

+ 236 - 145
o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/ConnectionAction.java

@@ -1,49 +1,53 @@
 package com.x.base.core.project.connection;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.TreeMap;
 
 import com.google.gson.Gson;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonParseException;
 import com.x.base.core.project.bean.NameValuePair;
 import com.x.base.core.project.gson.XGsonBuilder;
+import com.x.base.core.project.http.ActionResult.Type;
 import com.x.base.core.project.tools.DefaultCharset;
 import com.x.base.core.project.tools.ListTools;
+import com.x.base.core.project.tools.StringTools;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
-import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.entity.ContentType;
-import org.apache.http.entity.mime.MultipartEntityBuilder;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
-import org.apache.http.util.EntityUtils;
 
 public class ConnectionAction {
 
-	public static final String Access_Control_Allow_Credentials = "Access-Control-Allow-Credentials";
-	public static final String Access_Control_Allow_Credentials_Value = "true";
-	public static final String Access_Control_Allow_Headers = "Access-Control-Allow-Headers";
-	public static final String Access_Control_Allow_Headers_Value = "x-requested-with, x-request, x-token,Content-Type, x-cipher, x-client";
-	public static final String Access_Control_Allow_Methods = "Access-Control-Allow-Methods";
-	public static final String Access_Control_Allow_Methods_Value = "GET, POST, OPTIONS, PUT, DELETE, HEAD, TRACE";
+	private ConnectionAction() {
+	}
+
+	public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
+	public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE = "true";
+	public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
+	public static final String ACCESS_CONTROL_ALLOW_HEADERS_VALUE = "x-requested-with, x-request, x-token,Content-Type, x-cipher, x-client";
+	public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
+	public static final String ACCESS_CONTROL_ALLOW_METHODS_VALUE = "GET, POST, OPTIONS, PUT, DELETE, HEAD, TRACE";
 
-	public static final String Cache_Control = "Cache-Control";
-	public static final String Cache_Control_Value = "no-cache, no-transform";
-	public static final String Content_Type = "Content-Type";
-	public static final String Content_Type_Value = "application/json;charset=UTF-8";
+	public static final String CACHE_CONTROL = "Cache-Control";
+	public static final String CACHE_CONTROL_VALUE = "no-cache, no-transform";
+	public static final String CONTENT_TYPE = "Content-Type";
+	public static final String CONTENT_TYPE_VALUE = "application/json;charset=UTF-8";
+	public static final String CONTENT_LENGTH = "Content-Length";
 
 	public static final String METHOD_PUT = "PUT";
 	public static final String METHOD_POST = "POST";
@@ -52,88 +56,95 @@ public class ConnectionAction {
 
 	private static Gson gson = XGsonBuilder.instance();
 
-	public static ActionResponse get(String address, List<NameValuePair> heads) throws Exception {
+	private static ActionResponse getDelete(String address, String method, List<NameValuePair> heads) throws Exception {
 		ActionResponse response = new ActionResponse();
 		HttpURLConnection connection = null;
 		try {
 			URL url = new URL(address);
 			connection = (HttpURLConnection) url.openConnection();
 		} catch (Exception e) {
-			response.setType(ActionResponse.Type.connectFatal);
-			response.setMessage(
-					"ConnectionAction get create connection error, address:" + address + ", because:" + e.getMessage());
+			response.setType(Type.connectFatal);
+			response.setMessage(String.format("%s create connection error, address: %s, because: %s.", method, address,
+					e.getMessage()));
 			return response;
 		}
 		addHeads(connection, heads);
-		connection.setRequestMethod(METHOD_GET);
+		connection.setRequestMethod(method);
 		connection.setUseCaches(false);
 		connection.setDoOutput(false);
 		connection.setDoInput(true);
 		try {
-			/** 访问主机上的端口 */
 			connection.connect();
 		} catch (Exception e) {
-			response.setType(ActionResponse.Type.connectFatal);
-			response.setMessage(
-					"ConnectionAction get connect error, address:" + address + ", because:" + e.getMessage());
+			response.setType(Type.connectFatal);
+			response.setMessage(String.format("%s connect connection error, address: %s, because: %s.", method, address,
+					e.getMessage()));
 			return response;
 		}
 		return read(response, connection);
 	}
 
+	public static ActionResponse get(String address, List<NameValuePair> heads) throws Exception {
+		return getDelete(address, METHOD_GET, heads);
+	}
+
 	public static ActionResponse delete(String address, List<NameValuePair> heads) throws Exception {
-		ActionResponse response = new ActionResponse();
+		return getDelete(address, METHOD_DELETE, heads);
+	}
+
+	private static byte[] getDeleteBinary(String address, String method, List<NameValuePair> heads) throws Exception {
 		HttpURLConnection connection = null;
 		try {
 			URL url = new URL(address);
 			connection = (HttpURLConnection) url.openConnection();
 		} catch (Exception e) {
-			response.setType(ActionResponse.Type.connectFatal);
-			response.setMessage("ConnectionAction delete create connection error, address:" + address + ", because:"
-					+ e.getMessage());
-			return response;
+			throw new ExceptionGetBinary(e, connection);
 		}
 		addHeads(connection, heads);
-		connection.setRequestMethod(METHOD_DELETE);
+		connection.setRequestMethod(method);
 		connection.setUseCaches(false);
 		connection.setDoOutput(false);
 		connection.setDoInput(true);
 		try {
-			/** 访问主机上的端口 */
 			connection.connect();
 		} catch (Exception e) {
-			response.setType(ActionResponse.Type.connectFatal);
-			response.setMessage(
-					"ConnectionAction delete connect error, address:" + address + ", because:" + e.getMessage());
-			return response;
+			throw new ExceptionGetBinary(e, connection);
 		}
-		return read(response, connection);
+		return readBinary(connection);
 	}
 
-	public static ActionResponse post(String address, List<NameValuePair> heads, Object body) throws Exception {
+	public static byte[] getBinary(String address, List<NameValuePair> heads) throws Exception {
+		return getDeleteBinary(address, METHOD_GET, heads);
+	}
+
+	public static byte[] deleteBinary(String address, List<NameValuePair> heads) throws Exception {
+		return getDeleteBinary(address, METHOD_DELETE, heads);
+	}
+
+	public static ActionResponse postPut(String address, String method, List<NameValuePair> heads, Object body)
+			throws Exception {
 		ActionResponse response = new ActionResponse();
 		HttpURLConnection connection = null;
 		try {
 			URL url = new URL(address);
 			connection = (HttpURLConnection) url.openConnection();
 		} catch (Exception e) {
-			response.setType(ActionResponse.Type.connectFatal);
-			response.setMessage("create connection error, address:" + Objects.toString(connection.getURL())
-					+ ", method:" + connection.getRequestMethod() + ", because:" + e.getMessage() + ".");
+			response.setType(Type.connectFatal);
+			response.setMessage(String.format("%s create connection error, address: %s, because: %s.", method, address,
+					e.getMessage()));
 			return response;
 		}
 		addHeads(connection, heads);
-		connection.setRequestMethod(METHOD_POST);
+		connection.setRequestMethod(method);
 		connection.setUseCaches(false);
 		connection.setDoOutput(true);
 		connection.setDoInput(true);
 		try {
-			/** 访问主机上的端口 */
 			connection.connect();
 		} catch (Exception e) {
-			response.setType(ActionResponse.Type.connectFatal);
-			response.setMessage("connect error, address:" + Objects.toString(connection.getURL()) + ", method:"
-					+ connection.getRequestMethod() + ", because:" + e.getMessage() + ".");
+			response.setType(Type.connectFatal);
+			response.setMessage(
+					String.format("%s connect error, address: %s, because: %s.", method, address, e.getMessage()));
 			return response;
 		}
 		try (OutputStream output = connection.getOutputStream()) {
@@ -145,39 +156,40 @@ public class ConnectionAction {
 				}
 			}
 		} catch (Exception e) {
-			response.setType(ActionResponse.Type.connectFatal);
-			response.setMessage("output error, address:" + Objects.toString(connection.getURL()) + ", method:"
-					+ connection.getRequestMethod() + ", because:" + e.getMessage() + ".");
+			response.setType(Type.connectFatal);
+			response.setMessage(
+					String.format("%s ouput error, address: %s, because: %s.", method, address, e.getMessage()));
 			return response;
 		}
 		return read(response, connection);
 	}
 
+	public static ActionResponse post(String address, List<NameValuePair> heads, Object body) throws Exception {
+		return postPut(address, METHOD_POST, heads, body);
+	}
+
 	public static ActionResponse put(String address, List<NameValuePair> heads, Object body) throws Exception {
-		ActionResponse response = new ActionResponse();
+		return postPut(address, METHOD_PUT, heads, body);
+	}
+
+	private static byte[] postPutBinary(String address, String method, List<NameValuePair> heads, Object body)
+			throws Exception {
 		HttpURLConnection connection = null;
 		try {
 			URL url = new URL(address);
 			connection = (HttpURLConnection) url.openConnection();
 		} catch (Exception e) {
-			response.setType(ActionResponse.Type.connectFatal);
-			response.setMessage(
-					"ConnectionAction put create connection error, address:" + address + ", because:" + e.getMessage());
-			return response;
+			throw new ExceptionBinary(e, connection);
 		}
 		addHeads(connection, heads);
-		connection.setRequestMethod(METHOD_PUT);
+		connection.setRequestMethod(method);
 		connection.setUseCaches(false);
 		connection.setDoOutput(true);
 		connection.setDoInput(true);
 		try {
-			/** 访问主机上的端口 */
 			connection.connect();
 		} catch (Exception e) {
-			response.setType(ActionResponse.Type.connectFatal);
-			response.setMessage(
-					"ConnectionAction put connect error, address:" + address + ", because:" + e.getMessage());
-			return response;
+			throw new ExceptionBinary(e, connection);
 		}
 		try (OutputStream output = connection.getOutputStream()) {
 			if (null != body) {
@@ -188,16 +200,114 @@ public class ConnectionAction {
 				}
 			}
 		} catch (Exception e) {
-			response.setType(ActionResponse.Type.connectFatal);
-			response.setMessage(
-					"ConnectionAction put output error: [" + address + "], " + e.getClass().getName() + ".");
-			return response;
+			throw new ExceptionBinary(e, connection);
 		}
-		return read(response, connection);
+		return readBinary(connection);
+	}
+
+	public static byte[] postBinary(String address, List<NameValuePair> heads, Object body) throws Exception {
+		return postPutBinary(address, METHOD_POST, heads, body);
+	}
+
+	public static byte[] putBinary(String address, List<NameValuePair> heads, Object body) throws Exception {
+		return postPutBinary(address, METHOD_PUT, heads, body);
+	}
+
+	private static byte[] postPutMultiPartBinary(String address, String method, List<NameValuePair> heads,
+			Collection<FormField> formFields, Collection<FilePart> fileParts) throws Exception {
+		HttpURLConnection connection = null;
+		String boundary = StringTools.TWO_HYPHENS + StringTools.TWO_HYPHENS + System.currentTimeMillis();
+		try {
+			URL url = new URL(address);
+			connection = (HttpURLConnection) url.openConnection();
+		} catch (Exception e) {
+			throw new ExceptionMultiPartBinary(e, connection);
+		}
+		byte[] bytes = null;
+		try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
+			if (null != fileParts) {
+				for (FilePart filePart : fileParts) {
+					writeFilePart(byteArrayOutputStream, filePart, boundary);
+				}
+			}
+			if (null != formFields) {
+				for (FormField formField : formFields) {
+					writeFormField(byteArrayOutputStream, formField, boundary);
+				}
+			}
+			IOUtils.write(StringTools.TWO_HYPHENS + boundary + StringTools.TWO_HYPHENS, byteArrayOutputStream,
+					DefaultCharset.charset_utf_8);
+			bytes = byteArrayOutputStream.toByteArray();
+		} catch (Exception e) {
+			throw new ExceptionMultiPartBinary(e, connection);
+		}
+		addHeadsMultiPart(connection, heads, boundary);
+		connection.setRequestProperty(CONTENT_LENGTH, bytes.length + "");
+		connection.setRequestMethod(method);
+		connection.setUseCaches(false);
+		connection.setDoOutput(true);
+		connection.setDoInput(true);
+		try {
+			connection.connect();
+		} catch (Exception e) {
+			throw new ExceptionMultiPartBinary(e, connection);
+		}
+		try (OutputStream output = connection.getOutputStream()) {
+			IOUtils.write(bytes, output);
+		} catch (Exception e) {
+			throw new ExceptionMultiPartBinary(e, connection);
+		}
+		return readBinary(connection);
+	}
+
+	public static byte[] postMultiPartBinary(String address, List<NameValuePair> heads,
+			Collection<FormField> formFields, Collection<FilePart> fileParts) throws Exception {
+		return postPutMultiPartBinary(address, METHOD_POST, heads, formFields, fileParts);
+	}
+
+	public static byte[] putMultiPartBinary(String address, List<NameValuePair> heads, Collection<FormField> formFields,
+			Collection<FilePart> fileParts) throws Exception {
+		return postPutMultiPartBinary(address, METHOD_PUT, heads, formFields, fileParts);
+	}
+
+	private static void writeFormField(OutputStream output, FormField formField, String boundary) throws IOException {
+		IOUtils.write(StringTools.TWO_HYPHENS + boundary, output, StandardCharsets.UTF_8);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
+		IOUtils.write("Content-Disposition: form-data; name=\"" + formField.getName() + "\"", output,
+				StandardCharsets.UTF_8);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
+		IOUtils.write("Content-Length: " + formField.getValue().getBytes(StandardCharsets.UTF_8).length, output,
+				StandardCharsets.UTF_8);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
+		IOUtils.write("Content-Type: text/plain; charset=" + StandardCharsets.UTF_8.name(), output,
+				StandardCharsets.UTF_8);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
+		IOUtils.write(formField.getValue().getBytes(StandardCharsets.UTF_8), output);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
+	}
+
+	public static void writeFilePart(OutputStream output, FilePart filePart, String boundary) throws IOException {
+		IOUtils.write(StringTools.TWO_HYPHENS + boundary, output, StandardCharsets.UTF_8);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
+		IOUtils.write(
+				"Content-Disposition: form-data; name=\"" + filePart.getName().getBytes(StandardCharsets.UTF_8)
+						+ "\"; filename=\"" + filePart.getFileName().getBytes(StandardCharsets.UTF_8) + "\"",
+				output, StandardCharsets.UTF_8);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
+		IOUtils.write("Content-Length: " + filePart.getBytes().length, output, StandardCharsets.UTF_8);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
+		IOUtils.write("Content-Type: " + filePart.getContentType(), output, StandardCharsets.UTF_8);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
+		IOUtils.write(String.format("Content-Length: %d", filePart.getBytes().length), output, StandardCharsets.UTF_8);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
+		IOUtils.write(filePart.getBytes(), output);
+		IOUtils.write(StringTools.CRLF, output, StandardCharsets.UTF_8);
 	}
 
 	public static byte[] getFile(String address, List<NameValuePair> heads) throws Exception {
-		try(CloseableHttpClient httpclient = HttpClients.createDefault()){
+		try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
 			HttpGet httpget = new HttpGet(address);
 			if (ListTools.isNotEmpty(heads)) {
 				String name;
@@ -212,9 +322,9 @@ public class ConnectionAction {
 			}
 			HttpResponse response = httpclient.execute(httpget);
 			HttpEntity entity = response.getEntity();
-			if(entity!=null) {
+			if (entity != null) {
 				InputStream in = entity.getContent();
-				if (in != null){
+				if (in != null) {
 					return IOUtils.toByteArray(in);
 				}
 			}
@@ -222,53 +332,6 @@ public class ConnectionAction {
 		return null;
 	}
 
-	public static ActionResponse multiFormPost(String address, List<NameValuePair> heads, String fileName, byte[] bytes,
-			Map<String, String> map) throws Exception {
-		ActionResponse response = new ActionResponse();
-		try {
-			CloseableHttpClient httpClient = HttpClients.createDefault();
-			HttpPost uploadFile = new HttpPost(address);
-			MultipartEntityBuilder builder = MultipartEntityBuilder.create();
-			builder.addTextBody("fileName", fileName, ContentType.TEXT_PLAIN);
-			if (map != null) {
-				for (String key : map.keySet()) {
-					builder.addTextBody(key, map.get(key), ContentType.TEXT_PLAIN);
-				}
-			}
-			builder.addBinaryBody("file", bytes, ContentType.APPLICATION_OCTET_STREAM, fileName);
-			HttpEntity multipart = builder.build();
-			uploadFile.setEntity(multipart);
-			if (ListTools.isNotEmpty(heads)) {
-				String name;
-				String value;
-				for (NameValuePair o : heads) {
-					name = Objects.toString(o.getName(), "");
-					value = Objects.toString(o.getValue(), "");
-					if (StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
-						uploadFile.addHeader(name, value);
-					}
-				}
-			}
-			CloseableHttpResponse httpResponse = httpClient.execute(uploadFile);
-			HttpEntity responseEntity = httpResponse.getEntity();
-			String value = EntityUtils.toString(responseEntity, DefaultCharset.name);
-			try {
-				response = gson.fromJson(value, ActionResponse.class);
-			} catch (Exception e) {
-				response.setType(ActionResponse.Type.connectFatal);
-				response.setMessage("convert to json error, address:" + address + ", method: multiFormPost, because:"
-						+ e.getMessage() + ", value:" + value + ".");
-			}
-			httpClient.close();
-		} catch (Exception e) {
-			response.setType(ActionResponse.Type.connectFatal);
-			response.setMessage(
-					"ConnectionAction multiFormPost output error: [" + address + "], " + e.getClass().getName() + ".");
-			return response;
-		}
-		return response;
-	}
-
 	private static String extractErrorMessageIfExist(String str) {
 		if (StringUtils.isBlank(str)) {
 			return "";
@@ -282,9 +345,7 @@ public class ConnectionAction {
 				}
 			}
 		} catch (JsonParseException e) {
-			/*
-			 * pass
-			 */
+			// nothing
 		}
 		return str;
 	}
@@ -295,55 +356,85 @@ public class ConnectionAction {
 			try (InputStream input = connection.getErrorStream()) {
 				byte[] buffer = IOUtils.toByteArray(input);
 				response.setMessage(extractErrorMessageIfExist(new String(buffer, DefaultCharset.name)));
-				response.setType(ActionResponse.Type.error);
-			} catch (Exception e) {
-				response.setType(ActionResponse.Type.connectFatal);
-				response.setMessage("read input error, address:" + Objects.toString(connection.getURL()) + ", method:"
-						+ connection.getRequestMethod() + ", because:" + e.getMessage() + ".");
+				response.setType(Type.error);
 			}
 		} else if (code >= 400) {
-			response.setMessage(" url invalid error, address:" + Objects.toString(connection.getURL()) + ", method:"
-					+ connection.getRequestMethod() + ".");
-			response.setType(ActionResponse.Type.error);
+			response.setMessage(String.format("url invalid error, address: %s, method: %s, code: %d.",
+					Objects.toString(connection.getURL()), connection.getRequestMethod(), code));
+			response.setType(Type.error);
 		} else if (code == 200) {
 			try (InputStream input = connection.getInputStream()) {
 				byte[] buffer = IOUtils.toByteArray(input);
 				String value = new String(buffer, DefaultCharset.name);
-				try {
-					response = gson.fromJson(value, ActionResponse.class);
-				} catch (Exception e) {
-					response.setType(ActionResponse.Type.connectFatal);
-					response.setMessage("convert to json error, address:" + Objects.toString(connection.getURL())
-							+ ", method:" + connection.getRequestMethod() + ", because:" + e.getMessage() + ", value:"
-							+ value + ".");
-				}
+				response = gson.fromJson(value, ActionResponse.class);
 			} catch (Exception e) {
-				response.setType(ActionResponse.Type.connectFatal);
-				response.setMessage("read input error, address:" + Objects.toString(connection.getURL()) + ", method:"
-						+ connection.getRequestMethod() + ", because:" + e.getMessage() + ".");
+				response.setType(Type.connectFatal);
+				response.setMessage(String.format(
+						"convert input to json error, address: %s, method: %s, code: %d, because: %s.",
+						Objects.toString(connection.getURL()), connection.getRequestMethod(), code, e.getMessage()));
 			}
 		}
 		connection.disconnect();
 		return response;
 	}
 
+	private static byte[] readBinary(HttpURLConnection connection) throws Exception {
+		int code = connection.getResponseCode();
+		byte[] bytes = null;
+		if (code >= 500) {
+			try (InputStream input = connection.getErrorStream()) {
+				byte[] buffer = IOUtils.toByteArray(input);
+				throw new ExceptionReadBinary(connection.getURL(), connection.getRequestMethod(), code, buffer);
+			}
+		} else if (code >= 400) {
+			throw new ExceptionReadBinary(connection.getURL(), connection.getRequestMethod(), code);
+		} else if (code == 200) {
+			try (InputStream input = connection.getInputStream()) {
+				bytes = IOUtils.toByteArray(input);
+			} catch (Exception e) {
+				throw new ExceptionReadBinary(e, connection, code);
+			}
+		}
+		connection.disconnect();
+		return bytes;
+	}
+
 	private static void addHeads(HttpURLConnection connection, List<NameValuePair> heads) {
-		connection.setRequestProperty(Access_Control_Allow_Credentials, Access_Control_Allow_Credentials_Value);
-		connection.setRequestProperty(Access_Control_Allow_Headers, Access_Control_Allow_Headers_Value);
-		connection.setRequestProperty(Access_Control_Allow_Methods, Access_Control_Allow_Methods_Value);
-		connection.setRequestProperty(Cache_Control, Cache_Control_Value);
-		connection.setRequestProperty(Content_Type, Content_Type_Value);
+		Map<String, String> map = new TreeMap<>();
+		map.put(ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE);
+		map.put(ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_HEADERS_VALUE);
+		map.put(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_VALUE);
+		map.put(CACHE_CONTROL, CACHE_CONTROL_VALUE);
+		map.put(CONTENT_TYPE, CONTENT_TYPE_VALUE);
+		if (ListTools.isNotEmpty(heads)) {
+			String value;
+			for (NameValuePair o : heads) {
+				value = Objects.toString(o.getValue(), "");
+				if (StringUtils.isNotEmpty(o.getName()) && StringUtils.isNotEmpty(value)) {
+					map.put(o.getName(), value);
+				}
+			}
+		}
+		map.entrySet().forEach((o -> connection.addRequestProperty(o.getKey(), o.getValue())));
+	}
+
+	private static void addHeadsMultiPart(HttpURLConnection connection, List<NameValuePair> heads, String boundary) {
+		Map<String, String> map = new TreeMap<>();
+		map.put(ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE);
+		map.put(ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_HEADERS_VALUE);
+		map.put(ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_METHODS_VALUE);
+		map.put(CACHE_CONTROL, CACHE_CONTROL_VALUE);
+		connection.setRequestProperty(CONTENT_TYPE, String.format("multipart/form-data; boundary=%s", boundary));
 		if (ListTools.isNotEmpty(heads)) {
-			String name;
 			String value;
 			for (NameValuePair o : heads) {
-				name = Objects.toString(o.getName(), "");
 				value = Objects.toString(o.getValue(), "");
-				if (StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
-					connection.setRequestProperty(name, value);
+				if (StringUtils.isNotEmpty(o.getName()) && StringUtils.isNotEmpty(value)) {
+					map.put(o.getName(), value);
 				}
 			}
 		}
+		map.entrySet().forEach((o -> connection.addRequestProperty(o.getKey(), o.getValue())));
 	}
 
 }

+ 21 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/ExceptionBinary.java

@@ -0,0 +1,21 @@
+package com.x.base.core.project.connection;
+
+import java.net.HttpURLConnection;
+
+import com.x.base.core.project.exception.PromptException;
+
+public class ExceptionBinary extends PromptException {
+
+	private static final long serialVersionUID = 7551134321893884285L;
+
+	public ExceptionBinary(Throwable e, HttpURLConnection connection) {
+		super(e, "binary error, address: {}, method: {}, because: {}.", null == connection ? null : connection.getURL(),
+				null == connection ? null : connection.getRequestMethod(), e.getMessage());
+	}
+
+	public ExceptionBinary(HttpURLConnection connection) {
+		super("binary error, address: {}, method: {}.", null == connection ? null : connection.getURL(),
+				null == connection ? null : connection.getRequestMethod());
+	}
+
+}

+ 22 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/ExceptionDeleteBinary.java

@@ -0,0 +1,22 @@
+package com.x.base.core.project.connection;
+
+import java.net.HttpURLConnection;
+
+import com.x.base.core.project.exception.PromptException;
+
+public class ExceptionDeleteBinary extends PromptException {
+
+	private static final long serialVersionUID = 7551134321893884285L;
+
+	public ExceptionDeleteBinary(Throwable e, HttpURLConnection connection) {
+		super(e, "delete binary error, address: {}, method: {}, because: {}.",
+				null == connection ? null : connection.getURL(),
+				null == connection ? null : connection.getRequestMethod(), e.getMessage());
+	}
+
+	public ExceptionDeleteBinary(HttpURLConnection connection) {
+		super("delete binary error, address: {}, method: {}.", null == connection ? null : connection.getURL(),
+				null == connection ? null : connection.getRequestMethod());
+	}
+
+}

+ 22 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/ExceptionGetBinary.java

@@ -0,0 +1,22 @@
+package com.x.base.core.project.connection;
+
+import java.net.HttpURLConnection;
+
+import com.x.base.core.project.exception.PromptException;
+
+public class ExceptionGetBinary extends PromptException {
+
+	private static final long serialVersionUID = 7551134321893884285L;
+
+	public ExceptionGetBinary(Throwable e, HttpURLConnection connection) {
+		super(e, "get binary error, address: {}, method: {}, because: {}.",
+				null == connection ? null : connection.getURL(),
+				null == connection ? null : connection.getRequestMethod(), e.getMessage());
+	}
+
+	public ExceptionGetBinary(HttpURLConnection connection) {
+		super("get binary error, address: {}, method: {}.", null == connection ? null : connection.getURL(),
+				null == connection ? null : connection.getRequestMethod());
+	}
+
+}

+ 22 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/ExceptionMultiPartBinary.java

@@ -0,0 +1,22 @@
+package com.x.base.core.project.connection;
+
+import java.net.HttpURLConnection;
+
+import com.x.base.core.project.exception.PromptException;
+
+public class ExceptionMultiPartBinary extends PromptException {
+
+	private static final long serialVersionUID = 7551134321893884285L;
+
+	public ExceptionMultiPartBinary(Throwable e, HttpURLConnection connection) {
+		super(e, "multiPart binary error, address: {}, method: {}, because: {}.",
+				null == connection ? null : connection.getURL(),
+				null == connection ? null : connection.getRequestMethod(), e.getMessage());
+	}
+
+	public ExceptionMultiPartBinary(HttpURLConnection connection) {
+		super("multiPart binary error, address: {}, method: {}.", null == connection ? null : connection.getURL(),
+				null == connection ? null : connection.getRequestMethod());
+	}
+
+}

+ 26 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/ExceptionReadBinary.java

@@ -0,0 +1,26 @@
+package com.x.base.core.project.connection;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import com.x.base.core.project.exception.PromptException;
+
+public class ExceptionReadBinary extends PromptException {
+
+	private static final long serialVersionUID = 7551134321893884285L;
+
+	public ExceptionReadBinary(Throwable e, HttpURLConnection connection, int code) {
+		super(e, "read binary input error, address: {}, method: {}, code: {}, because: {}.",
+				null == connection ? null : connection.getURL(),
+				null == connection ? null : connection.getRequestMethod(), code, e.getMessage());
+	}
+
+	public ExceptionReadBinary(URL url, String method, int code) {
+		super("read binary input error, address: {}, method: {}, code: {}.", url, method, code);
+	}
+
+	public ExceptionReadBinary(URL url, String method, int code, byte[] bytes) {
+		super("read binary input error, address: {}, method: {}, code:{}, because: {}.", url, method, code,
+				new String(bytes));
+	}
+}

+ 57 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/FilePart.java

@@ -0,0 +1,57 @@
+package com.x.base.core.project.connection;
+
+import com.x.base.core.project.gson.GsonPropertyObject;
+import com.x.base.core.project.tools.StringTools;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class FilePart extends GsonPropertyObject {
+
+    public FilePart(String fileName, byte[] bytes, String contentType, String name) {
+        this.fileName = fileName;
+        this.bytes = bytes;
+        this.contentType = contentType;
+        this.name = name;
+    }
+
+    private String name;
+
+    private String fileName;
+
+    private byte[] bytes;
+
+    private String contentType;
+
+    public String getName() {
+        return StringUtils.isEmpty(name) ? "file_" + StringTools.uniqueToken() : name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public byte[] getBytes() {
+        return bytes;
+    }
+
+    public void setBytes(byte[] bytes) {
+        this.bytes = bytes;
+    }
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public void setContentType(String contentType) {
+        this.contentType = contentType;
+    }
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+
+}

+ 34 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/connection/FormField.java

@@ -0,0 +1,34 @@
+package com.x.base.core.project.connection;
+
+import java.util.Objects;
+
+import com.x.base.core.project.gson.GsonPropertyObject;
+
+public class FormField extends GsonPropertyObject {
+
+    public FormField(String name, Object value) {
+        this.name = name;
+        this.value = Objects.toString(value, "");
+    }
+
+    private String name;
+
+    private String value;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+}

+ 6 - 0
o2server/x_base_core_project/src/main/java/com/x/base/core/project/tools/StringTools.java

@@ -54,6 +54,12 @@ public class StringTools {
 
 	public static final char SQL_ESCAPE_CHAR = '^';
 
+	public static final String CRLF = "\r\n";
+	public static final String CR = "\r";
+	public static final String LF = "\n";
+
+	public static final String TWO_HYPHENS = "--";
+
 	private static final Random random = new Random();
 
 	public static int utf8Length(String str) {

+ 92 - 14
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/ActionDocToWordWorkOrWorkCompleted.java

@@ -5,6 +5,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.util.Date;
 import java.util.List;
+import java.util.Optional;
 
 import com.google.gson.JsonElement;
 import com.x.base.core.container.EntityManagerContainer;
@@ -13,7 +14,10 @@ import com.x.base.core.entity.annotation.CheckPersistType;
 import com.x.base.core.project.annotation.FieldDescribe;
 import com.x.base.core.project.config.Config;
 import com.x.base.core.project.config.ProcessPlatform;
+import com.x.base.core.project.config.ProcessPlatform.WorkCompletedExtensionEvent;
+import com.x.base.core.project.config.ProcessPlatform.WorkExtensionEvent;
 import com.x.base.core.project.config.StorageMapping;
+import com.x.base.core.project.connection.CipherConnectionAction;
 import com.x.base.core.project.exception.ExceptionAccessDenied;
 import com.x.base.core.project.exception.ExceptionEntityNotExist;
 import com.x.base.core.project.gson.GsonPropertyObject;
@@ -67,15 +71,72 @@ class ActionDocToWordWorkOrWorkCompleted extends BaseAction {
 
 	}
 
-	private Wo work(EffectivePerson effectivePerson, Wi wi, Work work) throws Exception {
-		String person = effectivePerson.isCipher() ? work.getCreatorPerson() : effectivePerson.getDistinguishedName();
+	private byte[] workConvert(EffectivePerson effectivePerson, Wi wi, String application, String process,
+			String activity) throws Exception {
+		byte[] bytes = null;
+		Optional<WorkExtensionEvent> event = Config.processPlatform().getExtensionEvents().getWorkDocToWordEvents()
+				.bind(application, process, activity);
+		if (event.isPresent()) {
+			bytes = this.workExtensionService(effectivePerson, wi.getContent(), event.get());
+		} else {
+			if (StringUtils.equals(ProcessPlatform.DOCTOWORDTYPE_CLOUD, Config.processPlatform().getDocToWordType())) {
+				bytes = DocumentTools.docToWord(wi.getFileName(), wi.getContent());
+			} else {
+				bytes = this.local(wi);
+			}
+		}
+		return bytes;
+	}
+
+	private byte[] workExtensionService(EffectivePerson effectivePerson, String content, WorkExtensionEvent event)
+			throws Exception {
+		byte[] bytes = null;
+		Req req = new Req();
+		req.setContent(content);
+		req.setPerson(effectivePerson.getDistinguishedName());
+		if (StringUtils.isNotEmpty(event.getCustom())) {
+			bytes = ThisApplication.context().applications().postQueryBinary(event.getCustom(), event.getUrl(), req);
+		} else {
+			bytes = CipherConnectionAction.postBinary(effectivePerson.getDebugger(), event.getUrl(), req);
+		}
+		return bytes;
+	}
+
+	private byte[] workCompletedConvert(EffectivePerson effectivePerson, Wi wi, String application, String process)
+			throws Exception {
 		byte[] bytes = null;
+		Optional<WorkCompletedExtensionEvent> event = Config.processPlatform().getExtensionEvents()
+				.getWorkCompletedDocToWordEvents().bind(application, process);
+		if (event.isPresent()) {
+			bytes = this.workCompletedExtensionService(effectivePerson, wi.getContent(), event.get());
+		} else {
+			if (StringUtils.equals(ProcessPlatform.DOCTOWORDTYPE_CLOUD, Config.processPlatform().getDocToWordType())) {
+				bytes = DocumentTools.docToWord(wi.getFileName(), wi.getContent());
+			} else {
+				bytes = this.local(wi);
+			}
+		}
+		return bytes;
+	}
 
-		if (StringUtils.equals(ProcessPlatform.DOCTOWORDTYPE_CLOUD, Config.processPlatform().getDocToWordType())) {
-			bytes = DocumentTools.docToWord(wi.getFileName(), wi.getContent());
+	private byte[] workCompletedExtensionService(EffectivePerson effectivePerson, String content,
+			WorkCompletedExtensionEvent event) throws Exception {
+		byte[] bytes = null;
+		Req req = new Req();
+		req.setContent(content);
+		req.setPerson(effectivePerson.getDistinguishedName());
+		if (StringUtils.isNotEmpty(event.getCustom())) {
+			bytes = ThisApplication.context().applications().postQueryBinary(event.getCustom(), event.getUrl(), req);
 		} else {
-			bytes = this.local(wi);
+			bytes = CipherConnectionAction.postBinary(effectivePerson.getDebugger(), event.getUrl(), req);
 		}
+		return bytes;
+	}
+
+	private Wo work(EffectivePerson effectivePerson, Wi wi, Work work) throws Exception {
+		String person = effectivePerson.isCipher() ? work.getCreatorPerson() : effectivePerson.getDistinguishedName();
+		byte[] bytes = this.workConvert(effectivePerson, wi, work.getApplication(), work.getProcess(),
+				work.getActivity());
 		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
 			List<Attachment> attachments = emc.listEqual(Attachment.class, Attachment.job_FIELDNAME, work.getJob());
 			Attachment attachment = null;
@@ -127,13 +188,8 @@ class ActionDocToWordWorkOrWorkCompleted extends BaseAction {
 	private Wo workCompleted(EffectivePerson effectivePerson, Wi wi, WorkCompleted workCompleted) throws Exception {
 		String person = effectivePerson.isCipher() ? workCompleted.getCreatorPerson()
 				: effectivePerson.getDistinguishedName();
-		byte[] bytes = null;
-
-		if (StringUtils.equals(ProcessPlatform.DOCTOWORDTYPE_CLOUD, Config.processPlatform().getDocToWordType())) {
-			bytes = DocumentTools.docToWord(wi.getFileName(), wi.getContent());
-		} else {
-			bytes = this.local(wi);
-		}
+		byte[] bytes = this.workCompletedConvert(effectivePerson, wi, workCompleted.getApplication(),
+				workCompleted.getProcess());
 		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
 			List<Attachment> attachments = emc.listEqual(Attachment.class, Attachment.job_FIELDNAME,
 					workCompleted.getJob());
@@ -170,8 +226,6 @@ class ActionDocToWordWorkOrWorkCompleted extends BaseAction {
 				attachment.setJob(workCompleted.getJob());
 				attachment.setActivity(workCompleted.getActivity());
 				attachment.setActivityName(workCompleted.getActivityName());
-				// attachment.setActivityToken(workCompleted.getActivityToken());
-				// attachment.setActivityType(workCompleted.getActivityType());
 				attachment.saveContent(mapping, bytes, wi.getFileName());
 				attachment.setType((new Tika()).detect(bytes, wi.getFileName()));
 				emc.persist(attachment, CheckPersistType.all);
@@ -230,4 +284,28 @@ class ActionDocToWordWorkOrWorkCompleted extends BaseAction {
 		}
 
 	}
+
+	public static class Req {
+
+		private String person;
+
+		private String content;
+
+		public String getPerson() {
+			return person;
+		}
+
+		public void setPerson(String person) {
+			this.person = person;
+		}
+
+		public String getContent() {
+			return content;
+		}
+
+		public void setContent(String content) {
+			this.content = content;
+		}
+	}
+
 }

+ 59 - 11
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/ActionDownloadWithWork.java

@@ -1,11 +1,13 @@
 package com.x.processplatform.assemble.surface.jaxrs.attachment;
 
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.lang3.BooleanUtils;
+import java.util.Optional;
 
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.config.ProcessPlatform.WorkExtensionEvent;
 import com.x.base.core.project.config.StorageMapping;
+import com.x.base.core.project.connection.CipherConnectionAction;
 import com.x.base.core.project.exception.ExceptionAccessDenied;
 import com.x.base.core.project.exception.ExceptionEntityNotExist;
 import com.x.base.core.project.http.ActionResult;
@@ -16,10 +18,14 @@ import com.x.processplatform.assemble.surface.ThisApplication;
 import com.x.processplatform.assemble.surface.WorkControl;
 import com.x.processplatform.core.entity.content.Attachment;
 import com.x.processplatform.core.entity.content.Work;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 
 class ActionDownloadWithWork extends BaseAction {
-	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, String workId, String fileName) throws Exception {
+	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, String workId, String fileName)
+			throws Exception {
 
 		ActionResult<Wo> result = new ActionResult<>();
 		Work work = null;
@@ -28,11 +34,9 @@ class ActionDownloadWithWork extends BaseAction {
 		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
 			Business business = new Business(emc);
 			work = emc.find(workId, Work.class);
-			/** 判断work是否存在 */
 			if (null == work) {
 				throw new ExceptionEntityNotExist(workId, Work.class);
 			}
-			/** 判断attachment是否存在 */
 			attachment = emc.find(id, Attachment.class);
 			if (null == attachment) {
 				throw new ExceptionEntityNotExist(id, Attachment.class);
@@ -45,20 +49,64 @@ class ActionDownloadWithWork extends BaseAction {
 		}
 		StorageMapping mapping = ThisApplication.context().storageMappings().get(Attachment.class,
 				attachment.getStorage());
-		if(StringUtils.isBlank(fileName)){
+		if (StringUtils.isBlank(fileName)) {
 			fileName = attachment.getName();
-		}else{
+		} else {
 			String extension = FilenameUtils.getExtension(fileName);
-			if(StringUtils.isEmpty(extension)){
-				fileName = fileName+ "." + attachment.getExtension();
+			if (StringUtils.isEmpty(extension)) {
+				fileName = fileName + "." + attachment.getExtension();
 			}
 		}
-		Wo wo = new Wo(attachment.readContent(mapping), this.contentType(false, fileName),
-				this.contentDisposition(false, fileName));
+		byte[] bytes = null;
+		Optional<WorkExtensionEvent> event = Config.processPlatform().getExtensionEvents()
+				.getWorkAttachmentDownloadEvents().bind(work.getApplication(), work.getProcess(), work.getActivity());
+		if (event.isPresent()) {
+			bytes = this.extensionService(effectivePerson, attachment.getId(), event.get());
+		} else {
+			bytes = attachment.readContent(mapping);
+		}
+		Wo wo = new Wo(bytes, this.contentType(false, fileName), this.contentDisposition(false, fileName));
 		result.setData(wo);
 		return result;
 	}
 
+	private byte[] extensionService(EffectivePerson effectivePerson, String id, WorkExtensionEvent event)
+			throws Exception {
+		byte[] bytes = null;
+		Req req = new Req();
+		req.setPerson(effectivePerson.getDistinguishedName());
+		req.setAttachment(id);
+		if (StringUtils.isNotEmpty(event.getCustom())) {
+			bytes = ThisApplication.context().applications().postQueryBinary(event.getCustom(), event.getUrl(), req);
+		} else {
+			bytes = CipherConnectionAction.postBinary(effectivePerson.getDebugger(), event.getUrl(), req);
+		}
+		return bytes;
+	}
+
+	public static class Req {
+
+		private String person;
+		private String attachment;
+
+		public String getPerson() {
+			return person;
+		}
+
+		public void setPerson(String person) {
+			this.person = person;
+		}
+
+		public String getAttachment() {
+			return attachment;
+		}
+
+		public void setAttachment(String attachment) {
+			this.attachment = attachment;
+		}
+
+	}
+
 	public static class Wo extends WoFile {
 
 		public Wo(byte[] bytes, String contentType, String contentDisposition) {

+ 62 - 12
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/ActionDownloadWithWorkCompleted.java

@@ -1,13 +1,14 @@
 package com.x.processplatform.assemble.surface.jaxrs.attachment;
 
 import java.util.List;
-
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.lang3.BooleanUtils;
+import java.util.Optional;
 
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.config.ProcessPlatform.WorkCompletedExtensionEvent;
 import com.x.base.core.project.config.StorageMapping;
+import com.x.base.core.project.connection.CipherConnectionAction;
 import com.x.base.core.project.exception.ExceptionEntityNotExist;
 import com.x.base.core.project.http.ActionResult;
 import com.x.base.core.project.http.EffectivePerson;
@@ -17,15 +18,19 @@ import com.x.processplatform.assemble.surface.ThisApplication;
 import com.x.processplatform.assemble.surface.WorkCompletedControl;
 import com.x.processplatform.core.entity.content.Attachment;
 import com.x.processplatform.core.entity.content.WorkCompleted;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 
 class ActionDownloadWithWorkCompleted extends BaseAction {
-	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, String workCompletedId, String fileName) throws Exception {
-		
+	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, String workCompletedId, String fileName)
+			throws Exception {
+
 		ActionResult<Wo> result = new ActionResult<>();
 		WorkCompleted workCompleted = null;
 		Attachment attachment = null;
-		
+
 		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
 			Business business = new Business(emc);
 			workCompleted = emc.find(workCompletedId, WorkCompleted.class);
@@ -49,20 +54,65 @@ class ActionDownloadWithWorkCompleted extends BaseAction {
 		}
 		StorageMapping mapping = ThisApplication.context().storageMappings().get(Attachment.class,
 				attachment.getStorage());
-		if(StringUtils.isBlank(fileName)){
+		if (StringUtils.isBlank(fileName)) {
 			fileName = attachment.getName();
-		}else{
+		} else {
 			String extension = FilenameUtils.getExtension(fileName);
-			if(StringUtils.isEmpty(extension)){
-				fileName = fileName+ "." + attachment.getExtension();
+			if (StringUtils.isEmpty(extension)) {
+				fileName = fileName + "." + attachment.getExtension();
 			}
 		}
-		Wo wo = new Wo(attachment.readContent(mapping), this.contentType(false, fileName),
-				this.contentDisposition(false, fileName));
+		byte[] bytes = null;
+		Optional<WorkCompletedExtensionEvent> event = Config.processPlatform().getExtensionEvents()
+				.getWorkCompletedAttachmentDownloadEvents()
+				.bind(workCompleted.getApplication(), workCompleted.getProcess());
+		if (event.isPresent()) {
+			bytes = this.extensionService(effectivePerson, attachment.getId(), event.get());
+		} else {
+			bytes = attachment.readContent(mapping);
+		}
+		Wo wo = new Wo(bytes, this.contentType(false, fileName), this.contentDisposition(false, fileName));
 		result.setData(wo);
 		return result;
 	}
 
+	private byte[] extensionService(EffectivePerson effectivePerson, String id, WorkCompletedExtensionEvent event)
+			throws Exception {
+		byte[] bytes = null;
+		Req req = new Req();
+		req.setPerson(effectivePerson.getDistinguishedName());
+		req.setAttachment(id);
+		if (StringUtils.isNotEmpty(event.getCustom())) {
+			bytes = ThisApplication.context().applications().postQueryBinary(event.getCustom(), event.getUrl(), req);
+		} else {
+			bytes = CipherConnectionAction.postBinary(effectivePerson.getDebugger(), event.getUrl(), req);
+		}
+		return bytes;
+	}
+
+	public static class Req {
+
+		private String person;
+		private String attachment;
+
+		public String getPerson() {
+			return person;
+		}
+
+		public void setPerson(String person) {
+			this.person = person;
+		}
+
+		public String getAttachment() {
+			return attachment;
+		}
+
+		public void setAttachment(String attachment) {
+			this.attachment = attachment;
+		}
+
+	}
+
 	public static class Wo extends WoFile {
 
 		public Wo(byte[] bytes, String contentType, String contentDisposition) {

+ 62 - 12
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/ActionDownloadWithWorkCompletedStream.java

@@ -1,13 +1,14 @@
 package com.x.processplatform.assemble.surface.jaxrs.attachment;
 
 import java.util.List;
-
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.lang3.BooleanUtils;
+import java.util.Optional;
 
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.config.ProcessPlatform.WorkCompletedExtensionEvent;
 import com.x.base.core.project.config.StorageMapping;
+import com.x.base.core.project.connection.CipherConnectionAction;
 import com.x.base.core.project.exception.ExceptionEntityNotExist;
 import com.x.base.core.project.http.ActionResult;
 import com.x.base.core.project.http.EffectivePerson;
@@ -17,15 +18,19 @@ import com.x.processplatform.assemble.surface.ThisApplication;
 import com.x.processplatform.assemble.surface.WorkCompletedControl;
 import com.x.processplatform.core.entity.content.Attachment;
 import com.x.processplatform.core.entity.content.WorkCompleted;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 
 class ActionDownloadWithWorkCompletedStream extends BaseAction {
-	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, String workCompletedId, String fileName) throws Exception {
-		
+	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, String workCompletedId, String fileName)
+			throws Exception {
+
 		ActionResult<Wo> result = new ActionResult<>();
 		WorkCompleted workCompleted = null;
 		Attachment attachment = null;
-	
+
 		try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
 			Business business = new Business(emc);
 			workCompleted = emc.find(workCompletedId, WorkCompleted.class);
@@ -49,20 +54,65 @@ class ActionDownloadWithWorkCompletedStream extends BaseAction {
 		}
 		StorageMapping mapping = ThisApplication.context().storageMappings().get(Attachment.class,
 				attachment.getStorage());
-		if(StringUtils.isBlank(fileName)){
+		if (StringUtils.isBlank(fileName)) {
 			fileName = attachment.getName();
-		}else{
+		} else {
 			String extension = FilenameUtils.getExtension(fileName);
-			if(StringUtils.isEmpty(extension)){
-				fileName = fileName+ "." + attachment.getExtension();
+			if (StringUtils.isEmpty(extension)) {
+				fileName = fileName + "." + attachment.getExtension();
 			}
 		}
-		Wo wo = new Wo(attachment.readContent(mapping), this.contentType(true, fileName),
-				this.contentDisposition(true, fileName));
+		byte[] bytes = null;
+		Optional<WorkCompletedExtensionEvent> event = Config.processPlatform().getExtensionEvents()
+				.getWorkCompletedAttachmentDownloadEvents()
+				.bind(workCompleted.getApplication(), workCompleted.getProcess());
+		if (event.isPresent()) {
+			bytes = this.extensionService(effectivePerson, attachment.getId(), event.get());
+		} else {
+			bytes = attachment.readContent(mapping);
+		}
+		Wo wo = new Wo(bytes, this.contentType(true, fileName), this.contentDisposition(true, fileName));
 		result.setData(wo);
 		return result;
 	}
 
+	private byte[] extensionService(EffectivePerson effectivePerson, String id, WorkCompletedExtensionEvent event)
+			throws Exception {
+		byte[] bytes = null;
+		Req req = new Req();
+		req.setPerson(effectivePerson.getDistinguishedName());
+		req.setAttachment(id);
+		if (StringUtils.isNotEmpty(event.getCustom())) {
+			bytes = ThisApplication.context().applications().postQueryBinary(event.getCustom(), event.getUrl(), req);
+		} else {
+			bytes = CipherConnectionAction.postBinary(effectivePerson.getDebugger(), event.getUrl(), req);
+		}
+		return bytes;
+	}
+
+	public static class Req {
+
+		private String person;
+		private String attachment;
+
+		public String getPerson() {
+			return person;
+		}
+
+		public void setPerson(String person) {
+			this.person = person;
+		}
+
+		public String getAttachment() {
+			return attachment;
+		}
+
+		public void setAttachment(String attachment) {
+			this.attachment = attachment;
+		}
+
+	}
+
 	public static class Wo extends WoFile {
 
 		public Wo(byte[] bytes, String contentType, String contentDisposition) {

+ 61 - 9
o2server/x_processplatform_assemble_surface/src/main/java/com/x/processplatform/assemble/surface/jaxrs/attachment/ActionDownloadWithWorkStream.java

@@ -1,11 +1,13 @@
 package com.x.processplatform.assemble.surface.jaxrs.attachment;
 
-import org.apache.commons.io.FilenameUtils;
-import org.apache.commons.lang3.BooleanUtils;
+import java.util.Optional;
 
 import com.x.base.core.container.EntityManagerContainer;
 import com.x.base.core.container.factory.EntityManagerContainerFactory;
+import com.x.base.core.project.config.Config;
+import com.x.base.core.project.config.ProcessPlatform.WorkExtensionEvent;
 import com.x.base.core.project.config.StorageMapping;
+import com.x.base.core.project.connection.CipherConnectionAction;
 import com.x.base.core.project.exception.ExceptionAccessDenied;
 import com.x.base.core.project.exception.ExceptionEntityNotExist;
 import com.x.base.core.project.http.ActionResult;
@@ -16,10 +18,14 @@ import com.x.processplatform.assemble.surface.ThisApplication;
 import com.x.processplatform.assemble.surface.WorkControl;
 import com.x.processplatform.core.entity.content.Attachment;
 import com.x.processplatform.core.entity.content.Work;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
 
 class ActionDownloadWithWorkStream extends BaseAction {
-	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, String workId, String fileName) throws Exception {
+	ActionResult<Wo> execute(EffectivePerson effectivePerson, String id, String workId, String fileName)
+			throws Exception {
 		ActionResult<Wo> result = new ActionResult<>();
 		Work work = null;
 		Attachment attachment = null;
@@ -44,20 +50,66 @@ class ActionDownloadWithWorkStream extends BaseAction {
 		}
 		StorageMapping mapping = ThisApplication.context().storageMappings().get(Attachment.class,
 				attachment.getStorage());
-		if(StringUtils.isBlank(fileName)){
+		if (StringUtils.isBlank(fileName)) {
 			fileName = attachment.getName();
-		}else{
+		} else {
 			String extension = FilenameUtils.getExtension(fileName);
-			if(StringUtils.isEmpty(extension)){
-				fileName = fileName+ "." + attachment.getExtension();
+			if (StringUtils.isEmpty(extension)) {
+				fileName = fileName + "." + attachment.getExtension();
 			}
 		}
-		Wo wo = new Wo(attachment.readContent(mapping), this.contentType(true, fileName),
-				this.contentDisposition(true, fileName));
+
+		byte[] bytes = null;
+		Optional<WorkExtensionEvent> event = Config.processPlatform().getExtensionEvents()
+				.getWorkAttachmentDownloadEvents().bind(work.getApplication(), work.getProcess(), work.getActivity());
+		if (event.isPresent()) {
+			bytes = this.extensionService(effectivePerson, attachment.getId(), event.get());
+		} else {
+			bytes = attachment.readContent(mapping);
+		}
+
+		Wo wo = new Wo(bytes, this.contentType(true, fileName), this.contentDisposition(true, fileName));
 		result.setData(wo);
 		return result;
 	}
 
+	private byte[] extensionService(EffectivePerson effectivePerson, String id, WorkExtensionEvent event)
+			throws Exception {
+		byte[] bytes = null;
+		Req req = new Req();
+		req.setPerson(effectivePerson.getDistinguishedName());
+		req.setAttachment(id);
+		if (StringUtils.isNotEmpty(event.getCustom())) {
+			bytes = ThisApplication.context().applications().postQueryBinary(event.getCustom(), event.getUrl(), req);
+		} else {
+			bytes = CipherConnectionAction.postBinary(effectivePerson.getDebugger(), event.getUrl(), req);
+		}
+		return bytes;
+	}
+
+	public static class Req {
+
+		private String person;
+		private String attachment;
+
+		public String getPerson() {
+			return person;
+		}
+
+		public void setPerson(String person) {
+			this.person = person;
+		}
+
+		public String getAttachment() {
+			return attachment;
+		}
+
+		public void setAttachment(String attachment) {
+			this.attachment = attachment;
+		}
+
+	}
+
 	public static class Wo extends WoFile {
 
 		public Wo(byte[] bytes, String contentType, String contentDisposition) {

+ 13 - 0
o2server/x_processplatform_service_processing/src/main/java/com/x/processplatform/service/processing/jaxrs/ExceptionFromMappingNotExist.java

@@ -0,0 +1,13 @@
+package com.x.processplatform.service.processing.jaxrs.work;
+
+import com.x.base.core.project.exception.PromptException;
+
+class ExceptionFromMappingNotExist extends PromptException {
+
+	private static final long serialVersionUID = -7038279889683420366L;
+
+	ExceptionFromMappingNotExist(String storage) {
+		super("from mapping not exist:{}.", storage);
+	}
+
+}