沙盒功能支持文件上传

1.x
tanghc 5 years ago
parent 682d3710ee
commit 22d87f7791
  1. 16
      doc/docs/files/90100_常见问题.md
  2. 2
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/configuration/AlipayServiceConfiguration.java
  3. 6
      sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/config/OpenServiceConfig.java
  4. 9
      sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/FileUploadDemoController.java
  5. 3
      sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/param/FileUploadParam.java
  6. 4
      sop-gateway/src/main/resources/application-dev.yml
  7. 6
      sop-sdk/sdk-java/src/main/java/com/gitee/sop/sdk/common/OpenConfig.java
  8. 5
      sop-website/website-front/pages/sandbox/sandbox.html
  9. 81
      sop-website/website-front/pages/sandbox/sandbox.js
  10. 2
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/DocItem.java
  11. 189
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/HttpTool.java
  12. 60
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/bean/UploadFile.java
  13. 5
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/config/WebsiteConfig.java
  14. 2
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/DocController.java
  15. 54
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/controller/SandboxController.java
  16. 2
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManager.java
  17. 34
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/DocManagerImpl.java
  18. 1
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/manager/SwaggerDocParser.java
  19. 65
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/util/FileUtil.java
  20. 38
      sop-website/website-server/src/main/java/com/gitee/sop/websiteserver/util/UploadUtil.java

@ -68,3 +68,19 @@ public static class Swagger2 extends SwaggerSupport {
} }
} }
``` ```
## 调试网关出现服务不可用
打断点调试,网关出现Read Timeout
参考:https://blog.csdn.net/qq_36872046/article/details/81058045
yml添加:
```yaml
ribbon:
# https://blog.csdn.net/qq_36872046/article/details/81058045
# 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。
# 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点
ReadTimeout: 60000
```

@ -14,7 +14,7 @@ import java.util.List;
*/ */
public class AlipayServiceConfiguration extends BaseServiceConfiguration { public class AlipayServiceConfiguration extends BaseServiceConfiguration {
public AlipayServiceConfiguration() { static {
// 默认版本号为1.0 // 默认版本号为1.0
ServiceConfig.getInstance().setDefaultVersion("1.0"); ServiceConfig.getInstance().setDefaultVersion("1.0");
} }

@ -28,7 +28,11 @@ public class OpenServiceConfig extends AlipayServiceConfiguration {
.addResourceLocations("classpath:/META-INF/resources/webjars/"); .addResourceLocations("classpath:/META-INF/resources/webjars/");
} }
// 开启文档
/**
* 开启文档
* http://ip:port/v2/api-docs
*/
@Configuration @Configuration
@EnableSwagger2 @EnableSwagger2
public static class Swagger2 extends SwaggerSupport { public static class Swagger2 extends SwaggerSupport {

@ -5,6 +5,10 @@ import com.gitee.sop.servercommon.util.UploadUtil;
import com.gitee.sop.storyweb.controller.param.FileUploadParam; import com.gitee.sop.storyweb.controller.param.FileUploadParam;
import com.gitee.sop.storyweb.controller.param.FileUploadParam2; import com.gitee.sop.storyweb.controller.param.FileUploadParam2;
import com.gitee.sop.storyweb.vo.FileUploadVO; import com.gitee.sop.storyweb.vo.FileUploadVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.Extension;
import io.swagger.annotations.ExtensionProperty;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -19,6 +23,7 @@ import java.util.Collection;
* @author tanghc * @author tanghc
*/ */
@RestController @RestController
@Api(tags = "文件上传")
public class FileUploadDemoController { public class FileUploadDemoController {
/** /**
@ -26,6 +31,7 @@ public class FileUploadDemoController {
* @param param * @param param
* @return * @return
*/ */
@ApiOperation(value = "文件上传例1", notes = "上传文件demo")
@ApiMapping(value = "demo.file.upload") @ApiMapping(value = "demo.file.upload")
public FileUploadVO file1(FileUploadParam param) { public FileUploadVO file1(FileUploadParam param) {
System.out.println(param.getRemark()); System.out.println(param.getRemark());
@ -48,6 +54,9 @@ public class FileUploadDemoController {
* @param param * @param param
* @return * @return
*/ */
@ApiOperation(value = "文件上传例2", notes = "可上传多个文件"
// 多文件上传、不确定文件数量上传,必须申明下面这句,否则沙盒界面不会出现上传控件
, extensions = @Extension(properties = @ExtensionProperty(name = "multiple", value = "multiple")))
@ApiMapping(value = "demo.file.upload2") @ApiMapping(value = "demo.file.upload2")
public FileUploadVO file2(FileUploadParam2 param, HttpServletRequest request) { public FileUploadVO file2(FileUploadParam2 param, HttpServletRequest request) {
System.out.println(param.getRemark()); System.out.println(param.getRemark());

@ -1,5 +1,6 @@
package com.gitee.sop.storyweb.controller.param; package com.gitee.sop.storyweb.controller.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -14,8 +15,10 @@ public class FileUploadParam {
// 上传文件,字段名称对应表单中的name属性值 // 上传文件,字段名称对应表单中的name属性值
@NotNull(message = "请上传文件1") @NotNull(message = "请上传文件1")
@ApiModelProperty(value = "文件1", required = true)
private MultipartFile file1; private MultipartFile file1;
@NotNull(message = "请上传文件2") @NotNull(message = "请上传文件2")
@ApiModelProperty(value = "文件2", required = true)
private MultipartFile file2; private MultipartFile file2;
} }

@ -15,6 +15,10 @@ zuul:
secret: MZZOUSTua6LzApIWXCwEgbBmxSzpzC secret: MZZOUSTua6LzApIWXCwEgbBmxSzpzC
ribbon: ribbon:
# https://blog.csdn.net/qq_36872046/article/details/81058045
# 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。
# 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点
ReadTimeout: 2000
# 设置为true(默认false),则所有请求都重试,默认只支持get请求重试 # 设置为true(默认false),则所有请求都重试,默认只支持get请求重试
# 请谨慎设置,因为post请求大多都是写入请求,如果要支持重试,确保服务的幂等性 # 请谨慎设置,因为post请求大多都是写入请求,如果要支持重试,确保服务的幂等性
OkToRetryOnAllOperations: false OkToRetryOnAllOperations: false

@ -50,11 +50,11 @@ public class OpenConfig {
private String errorResponseName = "error_response"; private String errorResponseName = "error_response";
/** 请求超时时间 */ /** 请求超时时间 */
private int connectTimeoutSeconds = 10; private int connectTimeoutSeconds = 60;
/** http读取超时时间 */ /** http读取超时时间 */
private int readTimeoutSeconds = 10; private int readTimeoutSeconds = 60;
/** http写超时时间 */ /** http写超时时间 */
private int writeTimeoutSeconds = 10; private int writeTimeoutSeconds = 60;
/** /**
* 构建数据节点名称 * 构建数据节点名称

@ -104,6 +104,9 @@
</div> </div>
<form class="layui-form" action=""> <form class="layui-form" action="">
<table id="treeTableReq" class="layui-table" lay-size="sm"></table> <table id="treeTableReq" class="layui-table" lay-size="sm"></table>
<div id="multipleDiv" style="margin-bottom: 20px;margin-top: 20px;display: none;">
<input id="multipleFile" name="file" type="file" multiple="multiple" value="上传文件" />
</div>
<button lay-submit lay-filter="formSend" class="layui-btn layui-btn-normal">发送请求</button> <button lay-submit lay-filter="formSend" class="layui-btn layui-btn-normal">发送请求</button>
</form> </form>
@ -136,7 +139,7 @@
<script src="../../assets/js/inputcache.js" type="text/javascript"></script> <script src="../../assets/js/inputcache.js" type="text/javascript"></script>
<script src="../doc/doc.js" type="text/javascript"></script> <script src="../doc/doc.js" type="text/javascript"></script>
<script src="sandbox.js" type="text/javascript"></script> <script src="sandbox.js?v=1.1" type="text/javascript"></script>
</body> </body>
</html> </html>

@ -20,6 +20,7 @@ function selectItem(docItem, layui) {
currentItem = docItem; currentItem = docItem;
resetResultDiv(); resetResultDiv();
var nameVersion = docItem.nameVersion; var nameVersion = docItem.nameVersion;
var multiple = docItem.multiple;
treetable = treetable || layui.treetable; treetable = treetable || layui.treetable;
$('.sop-name').text(docItem.name); $('.sop-name').text(docItem.name);
$('.sop-version').text(docItem.version); $('.sop-version').text(docItem.version);
@ -28,6 +29,8 @@ function selectItem(docItem, layui) {
createRequestParameter(docItem); createRequestParameter(docItem);
$('#multipleDiv').css('display', multiple ? 'block' : 'none');
var $li = $('#docItemTree').find('li[nameversion="'+nameVersion+'"]'); var $li = $('#docItemTree').find('li[nameversion="'+nameVersion+'"]');
$li.addClass('layui-this').siblings().removeClass('layui-this'); $li.addClass('layui-this').siblings().removeClass('layui-this');
InputCache.init(); InputCache.init();
@ -77,11 +80,12 @@ function createTreeTable(id, data) {
var id = currentItem.nameVersion + '-' + row.name; var id = currentItem.nameVersion + '-' + row.name;
var requiredTxt = row.required ? 'required lay-verify="required"' : ''; var requiredTxt = row.required ? 'required lay-verify="required"' : '';
var module = row.module; var module = row.module;
var type = row.type == 'file' ? 'file' : 'text';
var attrs = [ var attrs = [
'id="' + id + '"' 'id="' + id + '"'
, 'name="'+row.name+'"' , 'name="'+row.name+'"'
, 'class="layui-input test-input"' , 'class="layui-input test-input"'
, 'type="text"' , 'type="' + type + '"'
, requiredTxt , requiredTxt
, 'module="'+module+'"' , 'module="'+module+'"'
]; ];
@ -102,6 +106,7 @@ function doTest() {
method: method, method: method,
version: version version: version
}; };
var uploadFileObjects = getUploadFileObjects();
var $inputs = $body.find('.test-input'); var $inputs = $body.find('.test-input');
var bizContent = {}; var bizContent = {};
$inputs.each(function () { $inputs.each(function () {
@ -111,30 +116,90 @@ function doTest() {
bizContent[module] = {}; bizContent[module] = {};
} }
var moduleObj = bizContent[module]; var moduleObj = bizContent[module];
moduleObj[this.name] = this.value; putVal(moduleObj, this);
} else { } else {
bizContent[this.name] = this.value; putVal(bizContent, this);
} }
}); });
data.bizContent = JSON.stringify(bizContent); data.bizContent = JSON.stringify(bizContent);
// 确定文件数量,并且知道参数名称
if (uploadFileObjects.length > 0) {
var formData = new FormData();
for (var i = 0; i < uploadFileObjects.length; i++) {
var fileInput = uploadFileObjects[i];
formData.append(fileInput.name, fileInput.file);
}
postFile(data, formData);
} else {
postJson(data);
}
}
function putVal(obj, input) {
if (input.type == 'text') {
obj[input.name] = input.value;
}
}
function getUploadFileObjects() {
var fileObjects = [];
$body.find('input[type=file]')
.each(function () {
var $input = $(this);
var name = this.name;
var fileList = $input.prop('files');
var multiple = $input.prop('multiple');
if (multiple) {
for (var i = 0; i < fileList.length; i++) {
fileObjects.push({name: name + i, file: fileList[i]});
}
} else {
fileObjects.push({name: name, file: fileList[0]});
}
});
return fileObjects;
}
function postJson(data) {
$.ajax({ $.ajax({
url: SopConfig.url + '/sandbox/test' url: SopConfig.url + '/sandbox/test'
, dataType: 'json' , dataType: 'json'
, data: data , data: data
, method: 'post' , method: 'post'
, success: function (resp) { , success: successHandler
, error: errorHandler
});
}
function postFile(data, formData) {
for(var key in data) {
formData.append(key, data[key]);
}
$.ajax({
url: SopConfig.url + '/sandbox/test'
, data: formData
// ajax2.0可以不用设置请求头,但是jq帮我们自动设置了,这样的话需要我们自己取消掉
, contentType: false
// 取消帮我们格式化数据,是什么就是什么
, processData: false
, method: 'post'
, success: successHandler
, error: errorHandler
});
}
function successHandler(resp) {
setReqInfo(resp); setReqInfo(resp);
showRespnfo(resp.apiResult); showRespnfo(resp.apiResult);
} }
, error: function (xhr,status,error) {
function errorHandler(xhr,status,error) {
// {"timestamp":"2019-06-19 15:57:36","status":500,"error":"Internal Server Error","message":"appId不能为空","path":"/sandbox/test"} // {"timestamp":"2019-06-19 15:57:36","status":500,"error":"Internal Server Error","message":"appId不能为空","path":"/sandbox/test"}
var errorData = xhr.responseJSON; var errorData = xhr.responseJSON;
if (errorData) { if (errorData) {
setReqInfo(''); setReqInfo('');
showRespnfo(errorData.message); showRespnfo(errorData.message);
} }
}
});
} }
function showRespnfo(info) { function showRespnfo(info) {

@ -14,6 +14,8 @@ public class DocItem {
private String version; private String version;
private String summary; private String summary;
private String description; private String description;
// 是否多文件上传
private boolean multiple;
List<DocParameter> requestParameters; List<DocParameter> requestParameters;
List<DocParameter> responseParameters; List<DocParameter> responseParameters;

@ -0,0 +1,189 @@
package com.gitee.sop.websiteserver.bean;
import lombok.Data;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* @author tanghc
*/
public class HttpTool {
private static final String METHOD_GET = "get";
private Map<String, List<Cookie>> cookieStore = new HashMap<String, List<Cookie>>();
private OkHttpClient httpClient;
public HttpTool() {
this(new HttpToolConfig());
}
public HttpTool(HttpToolConfig httpToolConfig) {
this.initHttpClient(httpToolConfig);
}
protected void initHttpClient(HttpToolConfig httpToolConfig) {
httpClient = new OkHttpClient.Builder()
.connectTimeout(httpToolConfig.connectTimeoutSeconds, TimeUnit.SECONDS) // 设置链接超时时间,默认10秒
.readTimeout(httpToolConfig.readTimeoutSeconds, TimeUnit.SECONDS)
.writeTimeout(httpToolConfig.writeTimeoutSeconds, TimeUnit.SECONDS)
.cookieJar(new CookieJar() {
public void saveFromResponse(HttpUrl httpUrl, List<Cookie> list) {
cookieStore.put(httpUrl.host(), list);
}
public List<Cookie> loadForRequest(HttpUrl httpUrl) {
List<Cookie> cookies = cookieStore.get(httpUrl.host());
return cookies != null ? cookies : new ArrayList<Cookie>();
}
}).build();
}
@Data
public static class HttpToolConfig {
/** 请求超时时间 */
private int connectTimeoutSeconds = 10;
/** http读取超时时间 */
private int readTimeoutSeconds = 10;
/** http写超时时间 */
private int writeTimeoutSeconds = 10;
}
/**
* get请求
*
* @param url
* @param header
* @return
* @throws IOException
*/
public String get(String url, Map<String, String> header) throws IOException {
Request.Builder builder = new Request.Builder().url(url).get();
// 添加header
addHeader(builder, header);
Request request = builder.build();
Response response = httpClient.newCall(request).execute();
return response.body().string();
}
/**
* 提交表单
*
* @param url url
* @param form 参数
* @param header header
* @param method 请求方式postget等
* @return
* @throws IOException
*/
public String request(String url, Map<String, String> form, Map<String, String> header, String method) throws IOException {
Request.Builder requestBuilder;
if (METHOD_GET.equalsIgnoreCase(method)) {
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
for (Map.Entry<String, String> entry : form.entrySet()) {
urlBuilder.addQueryParameter(entry.getKey(), entry.getValue());
}
requestBuilder = new Request.Builder()
.url(urlBuilder.build())
.get();
} else {
FormBody.Builder paramBuilder = new FormBody.Builder(StandardCharsets.UTF_8);
for (Map.Entry<String, String> entry : form.entrySet()) {
paramBuilder.add(entry.getKey(), entry.getValue());
}
FormBody formBody = paramBuilder.build();
requestBuilder = new Request.Builder()
.url(url)
.method(method, formBody);
}
// 添加header
addHeader(requestBuilder, header);
Request request = requestBuilder.build();
Response response = httpClient
.newCall(request)
.execute();
try {
return response.body().string();
} finally {
response.close();
}
}
/**
* 提交表单并且上传文件
*
* @param url
* @param form
* @param header
* @param files
* @return
* @throws IOException
*/
public String requestFile(String url, Map<String, String> form, Map<String, String> header, List<UploadFile> files)
throws IOException {
// 创建MultipartBody.Builder,用于添加请求的数据
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder();
bodyBuilder.setType(MultipartBody.FORM);
for (UploadFile uploadFile : files) {
bodyBuilder.addFormDataPart(uploadFile.getName(), // 请求的名字
uploadFile.getFileName(), // 文件的文字,服务器端用来解析的
RequestBody.create(null, uploadFile.getFileData()) // 创建RequestBody,把上传的文件放入
);
}
Set<Map.Entry<String, String>> entrySet = form.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
bodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());
}
RequestBody requestBody = bodyBuilder.build();
Request.Builder builder = new Request.Builder().url(url).post(requestBody);
// 添加header
addHeader(builder, header);
Request request = builder.build();
Response response = httpClient.newCall(request).execute();
try {
return response.body().string();
} finally {
response.close();
}
}
private void addHeader(Request.Builder builder, Map<String, String> header) {
if (header != null) {
Set<Map.Entry<String, String>> entrySet = header.entrySet();
for (Map.Entry<String, String> entry : entrySet) {
builder.addHeader(entry.getKey(), String.valueOf(entry.getValue()));
}
}
}
public void setCookieStore(Map<String, List<Cookie>> cookieStore) {
this.cookieStore = cookieStore;
}
public void setHttpClient(OkHttpClient httpClient) {
this.httpClient = httpClient;
}
}

@ -0,0 +1,60 @@
package com.gitee.sop.websiteserver.bean;
import com.gitee.sop.websiteserver.util.FileUtil;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
/**
* 文件上传类
* @author tanghc
*/
@Getter
@Setter
public class UploadFile implements Serializable {
private static final long serialVersionUID = -1100614660944996398L;
/**
* @param name 表单名称不能重复
* @param file 文件
* @throws IOException
*/
public UploadFile(String name, File file) throws IOException {
this(name, file.getName(), FileUtil.toBytes(file));
}
/**
* @param name 表单名称不能重复
* @param fileName 文件名
* @param input 文件流
* @throws IOException
*/
public UploadFile(String name, String fileName, InputStream input) throws IOException {
this(name, fileName, FileUtil.toBytes(input));
}
/**
* @param name 表单名称不能重复
* @param fileName 文件名
* @param fileData 文件数据
*/
public UploadFile(String name, String fileName, byte[] fileData) {
super();
this.name = name;
this.fileName = fileName;
this.fileData = fileData;
this.md5 = DigestUtils.md5Hex(fileData);
}
private String name;
private String fileName;
private byte[] fileData;
private String md5;
}

@ -35,8 +35,11 @@ public class WebsiteConfig implements ApplicationRunner {
return new HttpMessageConverters(converter); return new HttpMessageConverters(converter);
} }
/**
* SpringBoot启动完毕执行
*/
@Override @Override
public void run(ApplicationArguments args) throws Exception { public void run(ApplicationArguments args) throws Exception {
docManager.load(); docManager.load(null);
} }
} }

@ -70,7 +70,7 @@ public class DocController {
public String reload(String pwd) { public String reload(String pwd) {
boolean correct = StringUtils.equals(this.pwd, pwd); boolean correct = StringUtils.equals(this.pwd, pwd);
if (correct) { if (correct) {
docManager.load(); docManager.load(null);
} }
return String.valueOf(correct); return String.valueOf(correct);
} }

@ -1,9 +1,13 @@
package com.gitee.sop.websiteserver.controller; package com.gitee.sop.websiteserver.controller;
import com.gitee.sop.websiteserver.bean.HttpTool;
import com.gitee.sop.websiteserver.bean.UploadFile;
import com.gitee.sop.websiteserver.sign.AlipayApiException; import com.gitee.sop.websiteserver.sign.AlipayApiException;
import com.gitee.sop.websiteserver.sign.AlipaySignature; import com.gitee.sop.websiteserver.sign.AlipaySignature;
import com.gitee.sop.websiteserver.util.UploadUtil;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.http.NameValuePair; import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.entity.UrlEncodedFormEntity;
@ -13,18 +17,24 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -39,13 +49,16 @@ public class SandboxController {
@Value("${api.url-sandbox}") @Value("${api.url-sandbox}")
private String url; private String url;
static HttpTool httpTool = new HttpTool();
@RequestMapping("/test") @RequestMapping("/test")
public SandboxResult proxy( public SandboxResult proxy(
@RequestParam String appId @RequestParam String appId
, @RequestParam String privateKey , @RequestParam String privateKey
, @RequestParam String method , @RequestParam String method
, @RequestParam String version , @RequestParam String version
, @RequestParam String bizContent) throws AlipayApiException { , @RequestParam String bizContent
, HttpServletRequest request) throws AlipayApiException {
Assert.isTrue(StringUtils.isNotBlank(appId), "AppId不能为空"); Assert.isTrue(StringUtils.isNotBlank(appId), "AppId不能为空");
Assert.isTrue(StringUtils.isNotBlank(privateKey), "PrivateKey不能为空"); Assert.isTrue(StringUtils.isNotBlank(privateKey), "PrivateKey不能为空");
@ -81,9 +94,31 @@ public class SandboxController {
params.put("sign", sign); params.put("sign", sign);
String responseData = get(url, params);// 发送请求 Collection<MultipartFile> uploadFiles = UploadUtil.getUploadFiles(request);
List<UploadFile> files = uploadFiles.stream()
.map(multipartFile -> {
try {
return new UploadFile(multipartFile.getName(), multipartFile.getOriginalFilename(), multipartFile.getBytes());
} catch (IOException e) {
log.error("封装文件失败", e);
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
try {
String responseData;
if (!CollectionUtils.isEmpty(files)) {
responseData = httpTool.requestFile(url, params, Collections.emptyMap(), files);
} else {
responseData = httpTool.request(url, params, Collections.emptyMap(), "get");
}
result.apiResult = responseData; result.apiResult = responseData;
return result; return result;
} catch (Exception e) {
throw new RuntimeException("请求失败");
}
} }
@Data @Data
@ -95,6 +130,7 @@ public class SandboxController {
private String apiResult; private String apiResult;
} }
/** /**
* 发送get请求 * 发送get请求
* @param url * @param url
@ -102,14 +138,10 @@ public class SandboxController {
* @throws Exception * @throws Exception
*/ */
public static String get(String url, Map<String, String> params) { public static String get(String url, Map<String, String> params) {
CloseableHttpClient client = null; CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null; CloseableHttpResponse response = null;
try{ try{
/** httpClient = HttpClients.createDefault();
* 创建一个httpclient对象
*/
client = HttpClients.createDefault();
List<NameValuePair> nameValuePairs = params.entrySet() List<NameValuePair> nameValuePairs = params.entrySet()
.stream() .stream()
.map(entry -> new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()))) .map(entry -> new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())))
@ -129,7 +161,7 @@ public class SandboxController {
/** /**
* 执行post请求 * 执行post请求
*/ */
response = client.execute(get); response = httpClient.execute(get);
/** /**
* 通过EntityUitls获取返回内容 * 通过EntityUitls获取返回内容
*/ */
@ -137,7 +169,7 @@ public class SandboxController {
}catch (Exception e){ }catch (Exception e){
e.printStackTrace(); e.printStackTrace();
}finally { }finally {
IOUtils.closeQuietly(client); IOUtils.closeQuietly(httpClient);
IOUtils.closeQuietly(response); IOUtils.closeQuietly(response);
} }
return null; return null;

@ -10,7 +10,7 @@ import java.util.Collection;
*/ */
public interface DocManager { public interface DocManager {
void load(); void load(String serviceId);
DocItem get(String method, String version); DocItem get(String method, String version);

@ -9,6 +9,7 @@ import com.gitee.sop.registryapi.service.RegistryService;
import com.gitee.sop.websiteserver.bean.DocInfo; import com.gitee.sop.websiteserver.bean.DocInfo;
import com.gitee.sop.websiteserver.bean.DocItem; import com.gitee.sop.websiteserver.bean.DocItem;
import com.gitee.sop.websiteserver.bean.ZookeeperContext; import com.gitee.sop.websiteserver.bean.ZookeeperContext;
import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent; import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -17,6 +18,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils; import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -66,7 +68,7 @@ public class DocManagerImpl implements DocManager {
private volatile boolean listenInited; private volatile boolean listenInited;
@Override @Override
public void load() { public void load(String serviceId) {
try { try {
List<ServiceInfo> serviceInfoList = registryService.listAllService(1, 9999); List<ServiceInfo> serviceInfoList = registryService.listAllService(1, 9999);
log.info("服务列表:{}", serviceInfoList); log.info("服务列表:{}", serviceInfoList);
@ -76,6 +78,12 @@ public class DocManagerImpl implements DocManager {
// 网关没有文档提供,需要排除 // 网关没有文档提供,需要排除
.filter(serviceInfo -> !"API-GATEWAY".equalsIgnoreCase(serviceInfo.getServiceId())) .filter(serviceInfo -> !"API-GATEWAY".equalsIgnoreCase(serviceInfo.getServiceId()))
.filter(serviceInfo -> !serviceInfo.getInstances().isEmpty()) .filter(serviceInfo -> !serviceInfo.getInstances().isEmpty())
.filter(serviceInfo -> {
if (StringUtils.isEmpty(serviceId)) {
return true;
}
return serviceId.equalsIgnoreCase(serviceInfo.getServiceId());
})
.map(serviceInfo -> serviceInfo.getInstances().get(0)) .map(serviceInfo -> serviceInfo.getInstances().get(0))
.collect(Collectors.toList()) .collect(Collectors.toList())
.forEach(this::loadDocInfo); .forEach(this::loadDocInfo);
@ -88,7 +96,7 @@ public class DocManagerImpl implements DocManager {
String query = this.buildQuery(); String query = this.buildQuery();
String url = "http://" + serviceInstance.getIp() + ":" + serviceInstance.getPort() + "/v2/api-docs" + query; String url = "http://" + serviceInstance.getIp() + ":" + serviceInstance.getPort() + "/v2/api-docs" + query;
try { try {
log.info("读取swagger文档,url:{}", url); log.info("读取swagger文档,serviceId:{}, url:{}", serviceInstance.getServiceId(), url);
ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class); ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class);
if (entity.getStatusCode() != HttpStatus.OK) { if (entity.getStatusCode() != HttpStatus.OK) {
throw new IllegalAccessException("无权访问"); throw new IllegalAccessException("无权访问");
@ -156,8 +164,15 @@ public class DocManagerImpl implements DocManager {
ZookeeperContext.listenChildren(routeRootPath, 1, (client, event) -> { ZookeeperContext.listenChildren(routeRootPath, 1, (client, event) -> {
if (listenInited) { if (listenInited) {
long id = System.currentTimeMillis(); long id = System.currentTimeMillis();
byte[] data = event.getData().getData();
String serviceInfoJson = new String(data);
ZKServiceInfo serviceInfo = JSON.parseObject(serviceInfoJson, ZKServiceInfo.class);
String serviceId = serviceInfo.getServiceId();
log.info("微服务[{}]推送更新", serviceId);
Msg msg = new Msg(id, 1000 * 20);
msg.serviceId = serviceId;
// 延迟20秒执行 // 延迟20秒执行
queue.offer(new Msg(id, 1000 * 20)); queue.offer(msg);
} }
TreeCacheEvent.Type type = event.getType(); TreeCacheEvent.Type type = event.getType();
if (type == TreeCacheEvent.Type.INITIALIZED) { if (type == TreeCacheEvent.Type.INITIALIZED) {
@ -169,6 +184,7 @@ public class DocManagerImpl implements DocManager {
static class Msg implements Delayed { static class Msg implements Delayed {
private long id; private long id;
private long delay; private long delay;
private String serviceId;
// 自定义实现比较方法返回 1 0 -1三个参数 // 自定义实现比较方法返回 1 0 -1三个参数
@ -192,6 +208,14 @@ public class DocManagerImpl implements DocManager {
} }
} }
@Data
static class ZKServiceInfo {
/** 服务名称,对应spring.application.name */
private String serviceId;
private String description;
}
@Slf4j @Slf4j
static class Consumer implements Runnable { static class Consumer implements Runnable {
private DelayQueue<Msg> queue; private DelayQueue<Msg> queue;
@ -206,9 +230,9 @@ public class DocManagerImpl implements DocManager {
public void run() { public void run() {
while (true) { while (true) {
try { try {
queue.take(); Msg msg = queue.take();
log.info("延迟队列触发--重新加载文档信息"); log.info("延迟队列触发--重新加载文档信息");
docManager.load(); docManager.load(msg.serviceId);
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }

@ -67,6 +67,7 @@ public class SwaggerDocParser implements DocParser {
docItem.setVersion(docInfo.getString("sop_version")); docItem.setVersion(docInfo.getString("sop_version"));
docItem.setSummary(docInfo.getString("summary")); docItem.setSummary(docInfo.getString("summary"));
docItem.setDescription(docInfo.getString("description")); docItem.setDescription(docInfo.getString("description"));
docItem.setMultiple(docInfo.getString("multiple") != null);
String moduleName = this.buildModuleName(docInfo, docRoot); String moduleName = this.buildModuleName(docInfo, docRoot);
docItem.setModule(moduleName); docItem.setModule(moduleName);
List<DocParameter> docParameterList = this.buildRequestParameterList(docInfo, docRoot); List<DocParameter> docParameterList = this.buildRequestParameterList(docInfo, docRoot);

@ -0,0 +1,65 @@
package com.gitee.sop.websiteserver.util;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class FileUtil {
/**
* The default buffer size to use.
*/
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
private static final int EOF = -1;
/**
* 将文件流转换成byte[]
* @param input
* @return
* @throws IOException
*/
public static byte[] toBytes(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
int n = 0;
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
}
return output.toByteArray();
}
/**
* 将文件转换成数据流
* @param file 文件
* @return 返回数据流
* @throws IOException
*/
public static byte[] toBytes(File file) throws IOException {
if (file.exists()) {
if (file.isDirectory()) {
throw new IOException("File '" + file + "' exists but is a directory");
}
if (file.canRead() == false) {
throw new IOException("File '" + file + "' cannot be read");
}
} else {
throw new FileNotFoundException("File '" + file + "' does not exist");
}
InputStream input = null;
try {
input = new FileInputStream(file);
return toBytes(input);
} finally {
try {
if (input != null) {
input.close();
}
} catch (IOException ioe) {
}
}
}
}

@ -0,0 +1,38 @@
package com.gitee.sop.websiteserver.util;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
/**
* 文件上传工具类
*
* @author tanghc
*/
public class UploadUtil {
/**
* 获取上传文件
*
* @param request
* @return
*/
public static Collection<MultipartFile> getUploadFiles(HttpServletRequest request) {
Map<String, MultipartFile> fileMap = null;
//检查form中是否有enctype="multipart/form-data"
if (ServletFileUpload.isMultipartContent(request)) {
//将request变成多部分request
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
fileMap = multiRequest.getFileMap();
}
return Optional.ofNullable(fileMap)
.map(map -> map.values())
.orElse(Collections.emptyList());
}
}
Loading…
Cancel
Save