mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2026-02-04 12:26:18 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97ae1a5e92 | ||
|
|
a4a521a6f8 | ||
|
|
2056a91071 | ||
|
|
f6209a8959 | ||
|
|
72ed0ea8f8 | ||
|
|
d698f82299 |
@@ -40,7 +40,7 @@ https://nfd-parser.github.io/nfd-preview/preview.html?src=https%3A%2F%2Flz.qaiu.
|
|||||||
|
|
||||||
**JavaScript解析器文档:** [JavaScript解析器开发指南](parser/doc/JAVASCRIPT_PARSER_GUIDE.md) | [自定义解析器扩展指南](parser/doc/CUSTOM_PARSER_GUIDE.md) | [快速开始](parser/doc/CUSTOM_PARSER_QUICKSTART.md)
|
**JavaScript解析器文档:** [JavaScript解析器开发指南](parser/doc/JAVASCRIPT_PARSER_GUIDE.md) | [自定义解析器扩展指南](parser/doc/CUSTOM_PARSER_GUIDE.md) | [快速开始](parser/doc/CUSTOM_PARSER_QUICKSTART.md)
|
||||||
|
|
||||||
**Playground功能:** [JS解析器演练场密码保护说明](PLAYGROUND_PASSWORD_PROTECTION.md)
|
**Playground功能:** [JS解析器演练场密码保护说明](web-service/doc/PLAYGROUND_PASSWORD_PROTECTION.md)
|
||||||
|
|
||||||
## 预览地址
|
## 预览地址
|
||||||
[预览地址1](https://lz.qaiu.top)
|
[预览地址1](https://lz.qaiu.top)
|
||||||
@@ -88,7 +88,9 @@ main分支依赖JDK17, 提供了JDK11分支[main-jdk11](https://github.com/qaiu/
|
|||||||
- Onedrive-pod
|
- Onedrive-pod
|
||||||
- Dropbox-pdp
|
- Dropbox-pdp
|
||||||
- iCloud-pic
|
- iCloud-pic
|
||||||
### 仅专属版提供
|
### 专属版提供
|
||||||
|
- [夸克云盘-qk](https://pan.quark.cn/)
|
||||||
|
- [UC云盘-uc](https://fast.uc.cn/)
|
||||||
- [移动云盘-p139](https://yun.139.com/)
|
- [移动云盘-p139](https://yun.139.com/)
|
||||||
- [联通云盘-pwo](https://pan.wo.cn/)
|
- [联通云盘-pwo](https://pan.wo.cn/)
|
||||||
- [天翼云盘-p189](https://cloud.189.cn/)
|
- [天翼云盘-p189](https://cloud.189.cn/)
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
package cn.qaiu.vx.core.verticle.conf;
|
|
||||||
|
|
||||||
import io.vertx.core.json.JsonObject;
|
|
||||||
import io.vertx.core.json.JsonArray;
|
|
||||||
import io.vertx.core.json.impl.JsonUtil;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converter and mapper for {@link cn.qaiu.vx.core.verticle.conf.HttpProxyConf}.
|
|
||||||
* NOTE: This class has been automatically generated from the {@link cn.qaiu.vx.core.verticle.conf.HttpProxyConf} original class using Vert.x codegen.
|
|
||||||
*/
|
|
||||||
public class HttpProxyConfConverter {
|
|
||||||
|
|
||||||
|
|
||||||
private static final Base64.Decoder BASE64_DECODER = JsonUtil.BASE64_DECODER;
|
|
||||||
private static final Base64.Encoder BASE64_ENCODER = JsonUtil.BASE64_ENCODER;
|
|
||||||
|
|
||||||
static void fromJson(Iterable<java.util.Map.Entry<String, Object>> json, HttpProxyConf obj) {
|
|
||||||
for (java.util.Map.Entry<String, Object> member : json) {
|
|
||||||
switch (member.getKey()) {
|
|
||||||
case "password":
|
|
||||||
if (member.getValue() instanceof String) {
|
|
||||||
obj.setPassword((String)member.getValue());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "port":
|
|
||||||
if (member.getValue() instanceof Number) {
|
|
||||||
obj.setPort(((Number)member.getValue()).intValue());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "preProxyOptions":
|
|
||||||
if (member.getValue() instanceof JsonObject) {
|
|
||||||
obj.setPreProxyOptions(new io.vertx.core.net.ProxyOptions((io.vertx.core.json.JsonObject)member.getValue()));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "timeout":
|
|
||||||
if (member.getValue() instanceof Number) {
|
|
||||||
obj.setTimeout(((Number)member.getValue()).intValue());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "username":
|
|
||||||
if (member.getValue() instanceof String) {
|
|
||||||
obj.setUsername((String)member.getValue());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toJson(HttpProxyConf obj, JsonObject json) {
|
|
||||||
toJson(obj, json.getMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toJson(HttpProxyConf obj, java.util.Map<String, Object> json) {
|
|
||||||
if (obj.getPassword() != null) {
|
|
||||||
json.put("password", obj.getPassword());
|
|
||||||
}
|
|
||||||
if (obj.getPort() != null) {
|
|
||||||
json.put("port", obj.getPort());
|
|
||||||
}
|
|
||||||
if (obj.getPreProxyOptions() != null) {
|
|
||||||
json.put("preProxyOptions", obj.getPreProxyOptions().toJson());
|
|
||||||
}
|
|
||||||
if (obj.getTimeout() != null) {
|
|
||||||
json.put("timeout", obj.getTimeout());
|
|
||||||
}
|
|
||||||
if (obj.getUsername() != null) {
|
|
||||||
json.put("username", obj.getUsername());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package cn.qaiu.vx.core.verticle.conf;
|
|
||||||
|
|
||||||
import io.vertx.codegen.annotations.DataObject;
|
|
||||||
import io.vertx.codegen.json.annotations.JsonGen;
|
|
||||||
import io.vertx.core.json.JsonObject;
|
|
||||||
import io.vertx.core.net.ProxyOptions;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@DataObject
|
|
||||||
@JsonGen(publicConverter = false)
|
|
||||||
public class HttpProxyConf {
|
|
||||||
|
|
||||||
public static final String DEFAULT_USERNAME = UUID.randomUUID().toString();
|
|
||||||
|
|
||||||
public static final String DEFAULT_PASSWORD = UUID.randomUUID().toString();
|
|
||||||
|
|
||||||
public static final Integer DEFAULT_PORT = 6402;
|
|
||||||
|
|
||||||
public static final Integer DEFAULT_TIMEOUT = 15000;
|
|
||||||
|
|
||||||
Integer timeout;
|
|
||||||
|
|
||||||
String username;
|
|
||||||
|
|
||||||
String password;
|
|
||||||
|
|
||||||
Integer port;
|
|
||||||
|
|
||||||
ProxyOptions preProxyOptions;
|
|
||||||
|
|
||||||
public HttpProxyConf() {
|
|
||||||
this.username = DEFAULT_USERNAME;
|
|
||||||
this.password = DEFAULT_PASSWORD;
|
|
||||||
this.timeout = DEFAULT_PORT;
|
|
||||||
this.timeout = DEFAULT_TIMEOUT;
|
|
||||||
this.preProxyOptions = new ProxyOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpProxyConf(JsonObject json) {
|
|
||||||
this();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Integer getTimeout() {
|
|
||||||
return timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpProxyConf setTimeout(Integer timeout) {
|
|
||||||
this.timeout = timeout;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpProxyConf setUsername(String username) {
|
|
||||||
this.username = username;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPassword() {
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpProxyConf setPassword(String password) {
|
|
||||||
this.password = password;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getPort() {
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpProxyConf setPort(Integer port) {
|
|
||||||
this.port = port;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProxyOptions getPreProxyOptions() {
|
|
||||||
return preProxyOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpProxyConf setPreProxyOptions(ProxyOptions preProxyOptions) {
|
|
||||||
this.preProxyOptions = preProxyOptions;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -263,7 +263,7 @@ public enum PanDomainTemplate {
|
|||||||
|
|
||||||
// https://pan-yz.cldisk.com/external/m/file/953658049102462976
|
// https://pan-yz.cldisk.com/external/m/file/953658049102462976
|
||||||
Pcx("超星云盘(需要referer头)",
|
Pcx("超星云盘(需要referer头)",
|
||||||
compile("https://pan-yz\\.cldisk\\.com/external/m/file/(?<KEY>\\w+)"),
|
compile("https://pan-yz\\.(chaoxing\\.com|cldisk\\.com)/external/m/file/(?<KEY>\\w+)(\\?.*)?"),
|
||||||
"https://pan-yz.cldisk.com/external/m/file/{shareKey}",
|
"https://pan-yz.cldisk.com/external/m/file/{shareKey}",
|
||||||
PcxTool.class),
|
PcxTool.class),
|
||||||
// WPS:分享格式:https://www.kdocs.cn/l/ck0azivLlDi3 ;API格式:https://www.kdocs.cn/api/office/file/{shareKey}/download
|
// WPS:分享格式:https://www.kdocs.cn/l/ck0azivLlDi3 ;API格式:https://www.kdocs.cn/api/office/file/{shareKey}/download
|
||||||
|
|||||||
@@ -294,67 +294,98 @@ public class LzTool extends PanBase {
|
|||||||
String sUrl = shareLinkInfo.getShareUrl();
|
String sUrl = shareLinkInfo.getShareUrl();
|
||||||
String pwd = shareLinkInfo.getSharePassword();
|
String pwd = shareLinkInfo.getSharePassword();
|
||||||
|
|
||||||
WebClient client = clientNoRedirects;
|
webClientSession.getAbs(sUrl).send().onSuccess(res -> {
|
||||||
client.getAbs(sUrl).send().onSuccess(res -> {
|
|
||||||
String html = res.bodyAsString();
|
String html = res.bodyAsString();
|
||||||
try {
|
// 检查是否需要 cookie 验证
|
||||||
String jsText = getJsByPwd(pwd, html, "var urls =window.location.href");
|
if (html.contains("var arg1='")) {
|
||||||
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "file");
|
webClientSession = WebClientSession.create(clientNoRedirects);
|
||||||
Map<String, Object> data = CastUtil.cast(scriptObjectMirror.get("data"));
|
setCookie(html);
|
||||||
MultiMap map = MultiMap.caseInsensitiveMultiMap();
|
// 重新请求
|
||||||
data.forEach((k, v) -> map.set(k, v.toString()));
|
webClientSession.getAbs(sUrl).send().onSuccess(res2 -> {
|
||||||
log.debug("解析参数: {}", map);
|
handleFileListParse(res2.bodyAsString(), pwd, sUrl, promise);
|
||||||
MultiMap headers = getHeaders(sUrl);
|
}).onFailure(err -> promise.fail(err));
|
||||||
|
return;
|
||||||
String url = SHARE_URL_PREFIX + "/filemoreajax.php?file=" + data.get("fid");
|
|
||||||
client.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res2 -> {
|
|
||||||
JsonObject fileListJson = asJson(res2);
|
|
||||||
if (fileListJson.getInteger("zt") != 1) {
|
|
||||||
promise.fail(baseMsg() + fileListJson.getString("info"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<FileInfo> list = new ArrayList<>();
|
|
||||||
fileListJson.getJsonArray("text").forEach(item -> {
|
|
||||||
/*
|
|
||||||
{
|
|
||||||
"icon": "apk",
|
|
||||||
"t": 0,
|
|
||||||
"id": "iULV2n4361c",
|
|
||||||
"name_all": "xx.apk",
|
|
||||||
"size": "49.8 M",
|
|
||||||
"time": "2021-03-19",
|
|
||||||
"duan": "in4361",
|
|
||||||
"p_ico": 0
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
JsonObject fileJson = (JsonObject) item;
|
|
||||||
FileInfo fileInfo = new FileInfo();
|
|
||||||
String size = fileJson.getString("size");
|
|
||||||
Long sizeNum = FileSizeConverter.convertToBytes(size);
|
|
||||||
String panType = shareLinkInfo.getType();
|
|
||||||
String id = fileJson.getString("id");
|
|
||||||
fileInfo.setFileName(fileJson.getString("name_all"))
|
|
||||||
.setFileId(id)
|
|
||||||
.setCreateTime(fileJson.getString("time"))
|
|
||||||
.setFileType(fileJson.getString("icon"))
|
|
||||||
.setSizeStr(fileJson.getString("size"))
|
|
||||||
.setSize(sizeNum)
|
|
||||||
.setPanType(panType)
|
|
||||||
.setParserUrl(getDomainName() + "/d/" + panType + "/" + id)
|
|
||||||
.setPreviewUrl(String.format("%s/v2/view/%s/%s", getDomainName(),
|
|
||||||
shareLinkInfo.getType(), id));
|
|
||||||
log.debug("文件信息: {}", fileInfo);
|
|
||||||
list.add(fileInfo);
|
|
||||||
});
|
|
||||||
promise.complete(list);
|
|
||||||
});
|
|
||||||
} catch (ScriptException | NoSuchMethodException e) {
|
|
||||||
promise.fail(e);
|
|
||||||
}
|
}
|
||||||
});
|
handleFileListParse(html, pwd, sUrl, promise);
|
||||||
|
}).onFailure(err -> promise.fail(err));
|
||||||
return promise.future();
|
return promise.future();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleFileListParse(String html, String pwd, String sUrl, Promise<List<FileInfo>> promise) {
|
||||||
|
try {
|
||||||
|
String jsText = getJsByPwd(pwd, html, "var urls =window.location.href");
|
||||||
|
ScriptObjectMirror scriptObjectMirror = JsExecUtils.executeDynamicJs(jsText, "file");
|
||||||
|
Map<String, Object> data = CastUtil.cast(scriptObjectMirror.get("data"));
|
||||||
|
MultiMap map = MultiMap.caseInsensitiveMultiMap();
|
||||||
|
data.forEach((k, v) -> map.set(k, v.toString()));
|
||||||
|
log.debug("解析参数: {}", map);
|
||||||
|
MultiMap headers = getHeaders(sUrl);
|
||||||
|
|
||||||
|
String url = SHARE_URL_PREFIX + "/filemoreajax.php?file=" + data.get("fid");
|
||||||
|
webClientSession.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res2 -> {
|
||||||
|
String resBody = asText(res2);
|
||||||
|
// 再次检查是否需要 cookie 验证
|
||||||
|
if (resBody.contains("var arg1='")) {
|
||||||
|
setCookie(resBody);
|
||||||
|
// 重新请求
|
||||||
|
webClientSession.postAbs(url).putHeaders(headers).sendForm(map).onSuccess(res3 -> {
|
||||||
|
handleFileListResponse(asText(res3), promise);
|
||||||
|
}).onFailure(err -> promise.fail(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleFileListResponse(resBody, promise);
|
||||||
|
}).onFailure(err -> promise.fail(err));
|
||||||
|
} catch (ScriptException | NoSuchMethodException e) {
|
||||||
|
promise.fail(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleFileListResponse(String responseBody, Promise<List<FileInfo>> promise) {
|
||||||
|
try {
|
||||||
|
JsonObject fileListJson = new JsonObject(responseBody);
|
||||||
|
if (fileListJson.getInteger("zt") != 1) {
|
||||||
|
promise.fail(baseMsg() + fileListJson.getString("info"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<FileInfo> list = new ArrayList<>();
|
||||||
|
fileListJson.getJsonArray("text").forEach(item -> {
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
"icon": "apk",
|
||||||
|
"t": 0,
|
||||||
|
"id": "iULV2n4361c",
|
||||||
|
"name_all": "xx.apk",
|
||||||
|
"size": "49.8 M",
|
||||||
|
"time": "2021-03-19",
|
||||||
|
"duan": "in4361",
|
||||||
|
"p_ico": 0
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
JsonObject fileJson = (JsonObject) item;
|
||||||
|
FileInfo fileInfo = new FileInfo();
|
||||||
|
String size = fileJson.getString("size");
|
||||||
|
Long sizeNum = FileSizeConverter.convertToBytes(size);
|
||||||
|
String panType = shareLinkInfo.getType();
|
||||||
|
String id = fileJson.getString("id");
|
||||||
|
fileInfo.setFileName(fileJson.getString("name_all"))
|
||||||
|
.setFileId(id)
|
||||||
|
.setCreateTime(fileJson.getString("time"))
|
||||||
|
.setFileType(fileJson.getString("icon"))
|
||||||
|
.setSizeStr(fileJson.getString("size"))
|
||||||
|
.setSize(sizeNum)
|
||||||
|
.setPanType(panType)
|
||||||
|
.setParserUrl(getDomainName() + "/d/" + panType + "/" + id)
|
||||||
|
.setPreviewUrl(String.format("%s/v2/view/%s/%s", getDomainName(),
|
||||||
|
shareLinkInfo.getType(), id));
|
||||||
|
log.debug("文件信息: {}", fileInfo);
|
||||||
|
list.add(fileInfo);
|
||||||
|
});
|
||||||
|
promise.complete(list);
|
||||||
|
} catch (Exception e) {
|
||||||
|
promise.fail(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setFileInfo(String html, ShareLinkInfo shareLinkInfo) {
|
void setFileInfo(String html, ShareLinkInfo shareLinkInfo) {
|
||||||
// 写入 fileInfo
|
// 写入 fileInfo
|
||||||
FileInfo fileInfo = new FileInfo();
|
FileInfo fileInfo = new FileInfo();
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package cn.qaiu.parser.impl;
|
package cn.qaiu.parser.impl;
|
||||||
|
|
||||||
|
import cn.qaiu.entity.FileInfo;
|
||||||
import cn.qaiu.entity.ShareLinkInfo;
|
import cn.qaiu.entity.ShareLinkInfo;
|
||||||
import cn.qaiu.parser.PanBase;
|
import cn.qaiu.parser.PanBase;
|
||||||
|
import cn.qaiu.util.FileSizeConverter;
|
||||||
import io.vertx.core.Future;
|
import io.vertx.core.Future;
|
||||||
import io.vertx.core.MultiMap;
|
|
||||||
import io.vertx.core.json.JsonObject;
|
import java.util.regex.Matcher;
|
||||||
import io.vertx.uritemplate.UriTemplate;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <a href="https://passport2.chaoxing.com">超星云盘</a>
|
* <a href="https://passport2.chaoxing.com">超星云盘</a>
|
||||||
@@ -19,24 +21,135 @@ public class PcxTool extends PanBase {
|
|||||||
public Future<String> parse() {
|
public Future<String> parse() {
|
||||||
client.getAbs(shareLinkInfo.getShareUrl())
|
client.getAbs(shareLinkInfo.getShareUrl())
|
||||||
.send().onSuccess(res -> {
|
.send().onSuccess(res -> {
|
||||||
// 'download': 'https://d0.ananas.chaoxing.com/download/de08dcf546e4dd88a17bead86ff6338d?at_=1740211698795&ak_=d62a3acbd5ce43e1e8565b67990691e4&ad_=8c4ef22e980ee0dd9532ec3757ab19f8&fn=33.c'
|
|
||||||
String body = res.bodyAsString();
|
String body = res.bodyAsString();
|
||||||
// 获取download
|
try {
|
||||||
String str = "var fileinfo = {";
|
// 提取文件信息
|
||||||
String fileInfo = res.bodyAsString().substring(res.bodyAsString().indexOf(str) + str.length() - 1
|
setFileInfo(body);
|
||||||
, res.bodyAsString().indexOf("};") + 1);
|
|
||||||
fileInfo = fileInfo.replace("'", "\"");
|
// 直接用正则提取download链接
|
||||||
JsonObject jsonObject = new JsonObject(fileInfo);
|
String download = extractDownloadUrl(body);
|
||||||
String download = jsonObject.getString("download");
|
if (download != null && download.contains("fn=")) {
|
||||||
if (download.contains("fn=")) {
|
complete(download);
|
||||||
complete(download);
|
} else {
|
||||||
} else {
|
fail("获取下载链接失败");
|
||||||
fail("获取下载链接失败: 不支持的文件类型: {}", jsonObject.getString("suffix"));
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail("解析文件信息失败: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
}).onFailure(handleFail(shareLinkInfo.getShareUrl()));
|
}).onFailure(handleFail(shareLinkInfo.getShareUrl()));
|
||||||
return promise.future();
|
return promise.future();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从HTML中提取download链接
|
||||||
|
*/
|
||||||
|
private String extractDownloadUrl(String html) {
|
||||||
|
// 匹配 'download': 'https://xxx' 或 "download": "https://xxx"
|
||||||
|
Pattern pattern = Pattern.compile("['\"]download['\"]\\s*:\\s*['\"]([^'\"]+)['\"]");
|
||||||
|
Matcher matcher = pattern.matcher(html);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return matcher.group(1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从HTML中提取文件信息并设置到shareLinkInfo
|
||||||
|
*/
|
||||||
|
private void setFileInfo(String html) {
|
||||||
|
try {
|
||||||
|
FileInfo fileInfo = new FileInfo();
|
||||||
|
|
||||||
|
// 提取文件名:从<title>标签或文件名input
|
||||||
|
String fileName = extractByRegex(html, "<title>([^<]+)</title>");
|
||||||
|
if (fileName == null) {
|
||||||
|
fileName = extractByRegex(html, "<input id=\"filename\" type=\"hidden\" value=\"([^\"]+)\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取文件大小:'filesize': 'xxx' 或 "filesize": "xxx"
|
||||||
|
String fileSizeStr = extractByRegex(html, "['\"]filesize['\"]\\s*:\\s*['\"]([^'\"]+)['\"]");
|
||||||
|
Long fileSize = null;
|
||||||
|
if (fileSizeStr != null) {
|
||||||
|
try {
|
||||||
|
fileSize = Long.parseLong(fileSizeStr);
|
||||||
|
} catch (NumberFormatException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取文件类型/后缀:'suffix': 'xxx' 或 "suffix": "xxx"
|
||||||
|
String suffix = extractByRegex(html, "['\"]suffix['\"]\\s*:\\s*['\"]([^'\"]+)['\"]");
|
||||||
|
|
||||||
|
// 提取objectId(文件ID):'objectId': 'xxx' 或 "objectId": "xxx"
|
||||||
|
String objectId = extractByRegex(html, "['\"]objectId['\"]\\s*:\\s*['\"]([^'\"]+)['\"]");
|
||||||
|
|
||||||
|
// 提取创建者:'creator': 'xxx' 或 "creator": "xxx"
|
||||||
|
String creator = extractByRegex(html, "['\"]creator['\"]\\s*:\\s*['\"]([^'\"]+)['\"]");
|
||||||
|
|
||||||
|
// 提取上传时间:'uploadDate': timestamp
|
||||||
|
String uploadDate = extractByRegex(html, "['\"]uploadDate['\"]\\s*:\\s*(\\d+)");
|
||||||
|
|
||||||
|
// 提取缩略图:'thumbnail': 'xxx' 或 "thumbnail": "xxx"
|
||||||
|
String thumbnail = extractByRegex(html, "['\"]thumbnail['\"]\\s*:\\s*['\"]([^'\"]+)['\"]");
|
||||||
|
|
||||||
|
// 设置文件信息
|
||||||
|
if (fileName != null) {
|
||||||
|
fileInfo.setFileName(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileSize != null) {
|
||||||
|
fileInfo.setSize(fileSize);
|
||||||
|
fileInfo.setSizeStr(FileSizeConverter.convertToReadableSize(fileSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suffix != null) {
|
||||||
|
fileInfo.setFileType(suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectId != null) {
|
||||||
|
fileInfo.setFileId(objectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creator != null) {
|
||||||
|
fileInfo.setCreateBy(creator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uploadDate != null) {
|
||||||
|
try {
|
||||||
|
long timestamp = Long.parseLong(uploadDate);
|
||||||
|
// 转换为日期格式
|
||||||
|
java.time.Instant instant = java.time.Instant.ofEpochMilli(timestamp);
|
||||||
|
java.time.LocalDateTime dateTime = java.time.LocalDateTime.ofInstant(instant,
|
||||||
|
java.time.ZoneId.systemDefault());
|
||||||
|
fileInfo.setCreateTime(dateTime.format(
|
||||||
|
java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
} catch (NumberFormatException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thumbnail != null) {
|
||||||
|
fileInfo.setPreviewUrl(thumbnail);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInfo.setPanType(shareLinkInfo.getType());
|
||||||
|
|
||||||
|
// 将文件信息存储到shareLinkInfo的otherParam中
|
||||||
|
shareLinkInfo.getOtherParam().put("fileInfo", fileInfo);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("提取文件信息失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用正则表达式提取内容
|
||||||
|
*/
|
||||||
|
private String extractByRegex(String text, String regex) {
|
||||||
|
Pattern pattern = Pattern.compile(regex);
|
||||||
|
Matcher matcher = pattern.matcher(text);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return matcher.group(1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// public static void main(String[] args) {
|
// public static void main(String[] args) {
|
||||||
// String s = new PcxTool(ShareLinkInfo.newBuilder().shareUrl("https://pan-yz.cldisk.com/external/m/file/953658049102462976")
|
// String s = new PcxTool(ShareLinkInfo.newBuilder().shareUrl("https://pan-yz.cldisk.com/external/m/file/953658049102462976")
|
||||||
|
|||||||
22
pom.xml
22
pom.xml
@@ -25,14 +25,16 @@
|
|||||||
|
|
||||||
<packageDirectory>${project.basedir}/web-service/target/package</packageDirectory>
|
<packageDirectory>${project.basedir}/web-service/target/package</packageDirectory>
|
||||||
|
|
||||||
<vertx.version>4.5.22</vertx.version>
|
<!-- Vert.x 4.5.24 已包含安全修复,无需单独指定 Netty 版本 -->
|
||||||
|
<vertx.version>4.5.14</vertx.version>
|
||||||
<org.reflections.version>0.10.2</org.reflections.version>
|
<org.reflections.version>0.10.2</org.reflections.version>
|
||||||
<lombok.version>1.18.38</lombok.version>
|
<lombok.version>1.18.38</lombok.version>
|
||||||
<slf4j.version>2.0.5</slf4j.version>
|
<slf4j.version>2.0.16</slf4j.version>
|
||||||
<commons-lang3.version>3.18.0</commons-lang3.version>
|
<commons-lang3.version>3.18.0</commons-lang3.version>
|
||||||
<commons-beanutils2.version>2.0.0</commons-beanutils2.version>
|
<commons-beanutils2.version>2.0.0</commons-beanutils2.version>
|
||||||
<jackson.version>2.14.2</jackson.version>
|
<jackson.version>2.18.2</jackson.version>
|
||||||
<logback.version>1.5.19</logback.version>
|
<!-- Logback 最新稳定版 -->
|
||||||
|
<logback.version>1.5.18</logback.version>
|
||||||
<junit.version>4.13.2</junit.version>
|
<junit.version>4.13.2</junit.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@@ -46,6 +48,18 @@
|
|||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 统一管理 logback 版本 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>${logback.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-core</artifactId>
|
||||||
|
<version>${logback.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.qaiu</groupId>
|
<groupId>cn.qaiu</groupId>
|
||||||
<artifactId>core</artifactId>
|
<artifactId>core</artifactId>
|
||||||
|
|||||||
Reference in New Issue
Block a user