init: 项目初始化
This commit is contained in:
+29
@@ -0,0 +1,29 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
*.log
|
||||
*.flattened-pom.xml
|
||||
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
### Mac files ###
|
||||
*.DS_Store
|
||||
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-parent</artifactId>
|
||||
<version>3.10.0</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
|
||||
<groupId>com.yxtech</groupId>
|
||||
<artifactId>ossutil</artifactId>
|
||||
<version>1.0</version>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<description>watch valid</description>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- AIO 服务器,高并发低开销 -->
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-boot-smarthttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-logging-logback-jakarta</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- HTTP 客户端,用于调用 IOSP OSS 接口 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.14</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpmime</artifactId>
|
||||
<version>4.5.14</version>
|
||||
</dependency>
|
||||
|
||||
<!-- YAML 配置支持 -->
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-config-yaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- JSON 解析 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>2.0.53</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>tencent</id>
|
||||
<url>https://mirrors.cloud.tencent.com/nexus/repository/maven-public/</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.yxtech.ossutil;
|
||||
|
||||
import org.noear.solon.Solon;
|
||||
import org.noear.solon.annotation.SolonMain;
|
||||
|
||||
@SolonMain
|
||||
public class App {
|
||||
public static void main(String[] args) {
|
||||
Solon.start(App.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.yxtech.ossutil.config;
|
||||
|
||||
import com.yxtech.ossutil.util.XjOssUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.noear.solon.annotation.Configuration;
|
||||
import org.noear.solon.annotation.Init;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
|
||||
/**
|
||||
* IOSP OSS 配置,从 app.yml 读取并注入到 XjOssUtil 静态字段
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class XjOssConfig {
|
||||
|
||||
@Inject("${xj.oss.gateway-url}")
|
||||
private String gatewayUrl;
|
||||
@Inject("${xj.oss.client-id}")
|
||||
private String clientId;
|
||||
@Inject("${xj.oss.client-secret}")
|
||||
private String clientSecret;
|
||||
@Inject("${xj.oss.organization-id}")
|
||||
private String organizationId;
|
||||
@Inject("${xj.oss.config-code}")
|
||||
private String configCode;
|
||||
@Inject("${xj.oss.directory}")
|
||||
private String directory;
|
||||
/** 逗号分隔的扩展名白名单,如 jpg,png,pdf;为空则不限制 */
|
||||
@Inject("${xj.oss.file-type-white-list:}")
|
||||
private String fileTypeWhiteList;
|
||||
|
||||
@Init
|
||||
public void init() {
|
||||
XjOssUtil.setGatewayUrl(gatewayUrl);
|
||||
XjOssUtil.setClientId(clientId);
|
||||
XjOssUtil.setClientSecret(clientSecret);
|
||||
XjOssUtil.setOrganizationId(organizationId);
|
||||
XjOssUtil.setConfigCode(configCode);
|
||||
XjOssUtil.setDirectory(directory);
|
||||
XjOssUtil.setFileTypeWhiteList(fileTypeWhiteList);
|
||||
log.info("[XjOssConfig] 初始化完成,网关: {},白名单: {}", gatewayUrl,
|
||||
fileTypeWhiteList.isBlank() ? "不限制" : fileTypeWhiteList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.yxtech.ossutil.controller;
|
||||
|
||||
import com.yxtech.ossutil.util.XjOssUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.noear.solon.annotation.Controller;
|
||||
import org.noear.solon.annotation.Get;
|
||||
import org.noear.solon.annotation.Mapping;
|
||||
import org.noear.solon.core.handle.Context;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* IOSP OSS 文件下载与预览
|
||||
* - /sys/upload/down/** 强制下载
|
||||
* - /sys/upload/show/** 浏览器内联预览
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
@Mapping("/")
|
||||
public class SysUploadController {
|
||||
|
||||
@Get
|
||||
@Mapping("/down/**")
|
||||
public void fileDown(Context ctx) throws Exception {
|
||||
serve(ctx, "/down/", true);
|
||||
}
|
||||
|
||||
@Get
|
||||
@Mapping("/show/**")
|
||||
public void fileShow(Context ctx) throws Exception {
|
||||
serve(ctx, "/show/", false);
|
||||
}
|
||||
|
||||
// ---- 核心逻辑 ----
|
||||
|
||||
private void serve(Context ctx, String marker, boolean download) throws Exception {
|
||||
String fileKey = extractFileKey(ctx, marker);
|
||||
if (isIllegalFileKey(fileKey)) {
|
||||
ctx.status(400);
|
||||
ctx.output("fileKey 格式非法");
|
||||
return;
|
||||
}
|
||||
String fileName = fileKey.contains("@") ? fileKey.substring(fileKey.indexOf("@") + 1) : fileKey;
|
||||
String ext = ext(fileName);
|
||||
|
||||
// 白名单校验
|
||||
try {
|
||||
XjOssUtil.checkExt(ext);
|
||||
} catch (IllegalArgumentException e) {
|
||||
ctx.status(403);
|
||||
ctx.output(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
try (InputStream in = XjOssUtil.down(fileKey)) {
|
||||
if (in == null) {
|
||||
ctx.status(404);
|
||||
ctx.output("文件不存在或下载失败");
|
||||
return;
|
||||
}
|
||||
String disposition = download ? "attachment" : "inline";
|
||||
ctx.contentType(download ? "application/octet-stream" : resolveContentType(ext));
|
||||
ctx.headerSet("Content-Disposition",
|
||||
disposition + ";filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
|
||||
try (OutputStream out = ctx.outputStream()) {
|
||||
byte[] buf = new byte[8192];
|
||||
int len;
|
||||
while ((len = in.read(buf)) != -1) out.write(buf, 0, len);
|
||||
out.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[SysUploadController] 文件服务失败, fileKey={}, download={}", fileKey, download, e);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 工具方法 ----
|
||||
|
||||
private String extractFileKey(Context ctx, String marker) {
|
||||
String path = ctx.path();
|
||||
int idx = path.indexOf(marker);
|
||||
if (idx < 0) return "";
|
||||
return URLDecoder.decode(path.substring(idx + marker.length()), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private boolean isIllegalFileKey(String fileKey) {
|
||||
if (fileKey == null || fileKey.isBlank()) return true;
|
||||
// 长度限制,防止资源耗尽
|
||||
if (fileKey.length() > 512) return true;
|
||||
// null byte 注入(可绕过扩展名校验)
|
||||
if (fileKey.contains("\0")) return true;
|
||||
// 绝对路径
|
||||
if (fileKey.startsWith("/") || fileKey.startsWith("\\")) return true;
|
||||
// 路径穿透:同时检查原始值和二次解码值(防双重编码绕过)
|
||||
String decoded = URLDecoder.decode(fileKey, StandardCharsets.UTF_8);
|
||||
return hasTraversal(fileKey) || hasTraversal(decoded);
|
||||
}
|
||||
|
||||
private boolean hasTraversal(String s) {
|
||||
return s.contains("../") || s.contains("..\\") || s.startsWith("..") || s.contains("/..");
|
||||
}
|
||||
|
||||
private String ext(String fileName) {
|
||||
int dot = fileName.lastIndexOf('.');
|
||||
return dot >= 0 ? fileName.substring(dot + 1).toLowerCase() : "";
|
||||
}
|
||||
|
||||
private String resolveContentType(String ext) {
|
||||
return switch (ext) {
|
||||
case "jpg", "jpeg" -> "image/jpeg";
|
||||
case "png" -> "image/png";
|
||||
case "gif" -> "image/gif";
|
||||
case "webp" -> "image/webp";
|
||||
case "bmp" -> "image/bmp";
|
||||
case "svg" -> "image/svg+xml";
|
||||
case "pdf" -> "application/pdf";
|
||||
case "mp4" -> "video/mp4";
|
||||
case "mp3" -> "audio/mpeg";
|
||||
case "txt" -> "text/plain;charset=UTF-8";
|
||||
case "html", "htm" -> "text/html;charset=UTF-8";
|
||||
default -> "application/octet-stream";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package com.yxtech.ossutil.util;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
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.client.utils.URIBuilder;
|
||||
import org.apache.http.entity.mime.HttpMultipartMode;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* IOSP OSS 文件工具类
|
||||
* - OAuth2 client_credentials 鉴权,内存缓存 token,401 自动重试
|
||||
* - 共享连接池,复用 TCP 连接
|
||||
* - 文件下载/预览,文件类型白名单校验
|
||||
*/
|
||||
@Slf4j
|
||||
public class XjOssUtil {
|
||||
|
||||
// ---- Setter ----
|
||||
// ---- 配置(由 XjOssConfig 注入) ----
|
||||
@Setter
|
||||
private static String gatewayUrl;
|
||||
@Setter
|
||||
private static String clientId;
|
||||
@Setter
|
||||
private static String clientSecret;
|
||||
@Setter
|
||||
private static String organizationId;
|
||||
@Setter
|
||||
private static String configCode;
|
||||
@Setter
|
||||
private static String directory;
|
||||
/** 允许访问的文件扩展名,空集合表示不限制 */
|
||||
private static Set<String> allowedExts = Collections.emptySet();
|
||||
|
||||
// ---- token 内存缓存 ----
|
||||
private static final AtomicReference<String> cachedToken = new AtomicReference<>();
|
||||
/** 过期时间戳(ms),提前 60s 刷新 */
|
||||
private static final AtomicLong tokenExpireAt = new AtomicLong(0);
|
||||
|
||||
// ---- 共享连接池 ----
|
||||
private static final PoolingHttpClientConnectionManager CM;
|
||||
/** 普通请求客户端(跟随重定向) */
|
||||
private static final CloseableHttpClient HTTP_CLIENT;
|
||||
/** 取 Location 专用客户端(禁止重定向) */
|
||||
private static final CloseableHttpClient NO_REDIRECT_CLIENT;
|
||||
|
||||
private static final RequestConfig REQUEST_CONFIG = RequestConfig.custom()
|
||||
.setConnectTimeout(10_000)
|
||||
.setSocketTimeout(10 * 60_000)
|
||||
.setConnectionRequestTimeout(3_000)
|
||||
.build();
|
||||
|
||||
static {
|
||||
CM = new PoolingHttpClientConnectionManager(60, TimeUnit.SECONDS);
|
||||
CM.setMaxTotal(200);
|
||||
CM.setDefaultMaxPerRoute(50);
|
||||
HTTP_CLIENT = HttpClients.custom()
|
||||
.setConnectionManager(CM)
|
||||
.setDefaultRequestConfig(REQUEST_CONFIG)
|
||||
.build();
|
||||
NO_REDIRECT_CLIENT = HttpClientBuilder.create()
|
||||
.setConnectionManager(CM)
|
||||
.setDefaultRequestConfig(REQUEST_CONFIG)
|
||||
.disableRedirectHandling()
|
||||
.build();
|
||||
}
|
||||
|
||||
public static void setFileTypeWhiteList(String v) {
|
||||
if (v == null || v.isBlank()) {
|
||||
allowedExts = Collections.emptySet();
|
||||
} else {
|
||||
allowedExts = Arrays.stream(v.split(","))
|
||||
.map(s -> s.trim().toLowerCase())
|
||||
.filter(s -> !s.isEmpty())
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 鉴权 ----
|
||||
|
||||
public static String getToken() {
|
||||
if (System.currentTimeMillis() < tokenExpireAt.get()) {
|
||||
return cachedToken.get();
|
||||
}
|
||||
return authenticate();
|
||||
}
|
||||
|
||||
public static synchronized String authenticate() {
|
||||
if (System.currentTimeMillis() < tokenExpireAt.get()) {
|
||||
return cachedToken.get();
|
||||
}
|
||||
HttpPost post = new HttpPost(gatewayUrl + "/oauth/oauth/token");
|
||||
post.setConfig(REQUEST_CONFIG);
|
||||
post.setEntity(MultipartEntityBuilder.create()
|
||||
.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
|
||||
.setCharset(StandardCharsets.UTF_8)
|
||||
.addTextBody("grant_type", "client_credentials")
|
||||
.addTextBody("client_id", clientId)
|
||||
.addTextBody("client_secret", clientSecret)
|
||||
.build());
|
||||
try (CloseableHttpResponse resp = HTTP_CLIENT.execute(post)) {
|
||||
int status = resp.getStatusLine().getStatusCode();
|
||||
if (status != HttpStatus.SC_OK) {
|
||||
log.error("[XjOssUtil] 鉴权失败,状态码: {}", status);
|
||||
return null;
|
||||
}
|
||||
JSONObject json = JSON.parseObject(EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8));
|
||||
String token = json.getString("access_token");
|
||||
int expiresIn = json.getIntValue("expires_in");
|
||||
cachedToken.set(token);
|
||||
tokenExpireAt.set(System.currentTimeMillis() + Math.max(expiresIn - 60, 30) * 1000L);
|
||||
log.info("[XjOssUtil] 鉴权成功,有效期 {}s", expiresIn);
|
||||
return token;
|
||||
} catch (Exception e) {
|
||||
log.error("[XjOssUtil] 鉴权异常", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 文件下载 ----
|
||||
|
||||
/**
|
||||
* 校验文件类型白名单,不合法时抛出 IllegalArgumentException
|
||||
*
|
||||
* @param ext 文件扩展名(小写,不含点)
|
||||
*/
|
||||
public static void checkExt(String ext) {
|
||||
if (!allowedExts.isEmpty() && !allowedExts.contains(ext)) {
|
||||
throw new IllegalArgumentException("不支持的文件类型: " + ext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件,返回文件输入流,调用方负责关闭
|
||||
*
|
||||
* @param fileKey 短文件 key,如 808fec...@aa.png
|
||||
*/
|
||||
public static InputStream down(String fileKey) {
|
||||
// 防御性校验(Controller 已校验,此处作为纵深防御)
|
||||
if (fileKey == null || fileKey.isBlank() || fileKey.contains("..") || fileKey.contains("\0")) {
|
||||
log.warn("[XjOssUtil] 拒绝非法 fileKey");
|
||||
return null;
|
||||
}
|
||||
String locationUrl = getLocationUrl(buildFullFileKey(fileKey), false);
|
||||
if (locationUrl == null) return null;
|
||||
// SSRF 防护:只允许 http/https 协议
|
||||
if (!locationUrl.startsWith("http://") && !locationUrl.startsWith("https://")) {
|
||||
log.error("[XjOssUtil] Location URL 协议非法,拒绝请求: {}", locationUrl);
|
||||
return null;
|
||||
}
|
||||
HttpGet get = new HttpGet(locationUrl);
|
||||
get.setConfig(REQUEST_CONFIG);
|
||||
try {
|
||||
CloseableHttpResponse resp = HTTP_CLIENT.execute(get);
|
||||
int status = resp.getStatusLine().getStatusCode();
|
||||
if (status != HttpStatus.SC_OK) {
|
||||
log.error("[XjOssUtil] 获取文件流失败,状态码: {}", status);
|
||||
resp.close();
|
||||
return null;
|
||||
}
|
||||
return new ResponseInputStream(resp);
|
||||
} catch (Exception e) {
|
||||
log.error("[XjOssUtil] 文件下载异常", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 私有方法 ----
|
||||
|
||||
private static String buildFullFileKey(String fileKey) {
|
||||
String dir = directory.endsWith("/") ? directory : directory + "/";
|
||||
return dir + "0/" + fileKey;
|
||||
}
|
||||
|
||||
private static String getLocationUrl(String fullFileKey, boolean retried) {
|
||||
try {
|
||||
URI uri = new URIBuilder(gatewayUrl + "/hfle/v2/" + organizationId + "/files/" + configCode + "/download-by-key")
|
||||
.addParameter("fileKey", fullFileKey).build();
|
||||
HttpGet get = new HttpGet(uri);
|
||||
get.setConfig(REQUEST_CONFIG);
|
||||
get.addHeader("Authorization", "Bearer " + getToken());
|
||||
try (CloseableHttpResponse resp = NO_REDIRECT_CLIENT.execute(get)) {
|
||||
int status = resp.getStatusLine().getStatusCode();
|
||||
if (status == HttpStatus.SC_UNAUTHORIZED && !retried) {
|
||||
log.warn("[XjOssUtil] token 过期,重新鉴权后重试");
|
||||
tokenExpireAt.set(0);
|
||||
return getLocationUrl(fullFileKey, true);
|
||||
}
|
||||
if (status != HttpStatus.SC_MOVED_TEMPORARILY && status != HttpStatus.SC_SEE_OTHER) {
|
||||
log.error("[XjOssUtil] download-by-key 非预期状态码: {}", status);
|
||||
return null;
|
||||
}
|
||||
Header location = resp.getFirstHeader("Location");
|
||||
if (location == null) {
|
||||
log.error("[XjOssUtil] download-by-key 未返回 Location");
|
||||
return null;
|
||||
}
|
||||
return location.getValue();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[XjOssUtil] 获取 Location 异常", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 内部类:包装响应流,close 时归还连接 ----
|
||||
|
||||
private static class ResponseInputStream extends InputStream {
|
||||
private final InputStream body;
|
||||
private final CloseableHttpResponse response;
|
||||
|
||||
ResponseInputStream(CloseableHttpResponse response) throws IOException {
|
||||
this.response = response;
|
||||
this.body = response.getEntity().getContent();
|
||||
}
|
||||
|
||||
@Override public int read() throws IOException { return body.read(); }
|
||||
@Override public int read(byte[] b, int off, int len) throws IOException { return body.read(b, off, len); }
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try { body.close(); } finally { response.close(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
server.port: 8080
|
||||
solon.config.load:
|
||||
- "app-env.yml" #可以是环境相关的
|
||||
solon.app:
|
||||
name: 'ossutil'
|
||||
group: 'yxtech'
|
||||
|
||||
solon.logging:
|
||||
appender:
|
||||
console:
|
||||
level: INFO
|
||||
file:
|
||||
level: INFO
|
||||
|
||||
# IOSP OSS 配置
|
||||
xj:
|
||||
oss:
|
||||
# gateway-url: http://api.iosp.xjyt.petrochina
|
||||
gateway-url: http://api.iosp.ydpt.tech
|
||||
client-id: jkglpt
|
||||
client-secret: jkglpt@12345!
|
||||
organization-id: 0
|
||||
config-code: FILE-OSS-0a85c4dd40ca745a598b872c7974bcaca
|
||||
directory: scyx/scfz/aygc/jkgl/
|
||||
# 允许访问的文件类型,逗号分隔;留空则不限制
|
||||
file-type-white-list: json,dat,svg,dwg,pr,pptm,ai,bak,pptx,lic,db,caj,wps,xmind,eps,exe,doc,docx,xls,xlsx,ppt,pps,pdf,xml,webp,MOV,MP4,HEVC,wmv,rmvb,rm,odt,3gp,swf,gz,tgz,log,bz,bz2,tbz,zip,rar,tar,7z,mp3,mid,ogg,mpga,mp4a,wav,wma,avi,dv,mp4,bmp,xlsm,dps,bak,cdr,et,kdh,nh,,nh,mpeg,mpg,mov,wm,flv,mkv,bmp,jpg,jpeg,gif,png,tif,tiff,tga,psd,txt,php,html,htm,js,css,rtf,rtfd,py,java,rb,sh,pl,sql,xml,gdb,gdbx
|
||||
@@ -0,0 +1,19 @@
|
||||
package features;
|
||||
|
||||
import com.yxtech.ossutil.App;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.noear.solon.test.HttpTester;
|
||||
import org.noear.solon.test.SolonTest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@SolonTest(App.class)
|
||||
public class HelloTest extends HttpTester {
|
||||
@Test
|
||||
public void hello() throws IOException {
|
||||
assert path("/hello?name=world").get().contains("world");
|
||||
assert path("/hello?name=solon").get().contains("solon");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user