2025.4.23 filebrowser生成文件分享链接,普通人也可直接下载,无需token
This commit is contained in:
parent
0b58fa9969
commit
1497a29c75
@ -1,4 +1,6 @@
|
|||||||
package com.sky.utils;
|
package com.sky.utils;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -12,6 +14,7 @@ import org.apache.http.entity.StringEntity;
|
|||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
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.util.EntityUtils;
|
import org.apache.http.util.EntityUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
@ -27,46 +30,38 @@ public class FileBrowserUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* —— 第一步:登录拿 token ——
|
* —— 第一步:登录拿 token ——
|
||||||
* 调用 /api/login 接口,返回原始的 JWT token 字符串
|
* 调用 /api/login 接口,返回纯 JWT 字符串,或 {"token":"..."} 结构
|
||||||
* curl -X POST "https://fshare.bitday.top/api/login" \
|
|
||||||
* -H "Content-Type: application/json" \
|
|
||||||
* -d '{
|
|
||||||
* "username":"admin",
|
|
||||||
* "password":"asdf14789"
|
|
||||||
* }'
|
|
||||||
* 返回值: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public String login() throws IOException {
|
public String login() throws IOException {
|
||||||
String url = domain + "/api/login";
|
String url = domain + "/api/login";
|
||||||
// 创建 HttpClient 实例
|
try (CloseableHttpClient client = HttpClients.createDefault()) {
|
||||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
HttpPost post = new HttpPost(url);
|
||||||
// 构造 POST 请求
|
post.setHeader("Content-Type", "application/json");
|
||||||
HttpPost httpPost = new HttpPost(url);
|
|
||||||
httpPost.setHeader("Content-Type", "application/json");
|
|
||||||
|
|
||||||
// 构造 JSON body
|
// 构造登录参数
|
||||||
JSONObject json = new JSONObject();
|
JSONObject cred = new JSONObject();
|
||||||
json.put("username", username);
|
cred.put("username", username);
|
||||||
json.put("password", password);
|
cred.put("password", password);
|
||||||
|
post.setEntity(new StringEntity(cred.toString(), StandardCharsets.UTF_8));
|
||||||
|
|
||||||
// 设置请求体
|
try (CloseableHttpResponse resp = client.execute(post)) {
|
||||||
StringEntity requestEntity = new StringEntity(json.toString(), StandardCharsets.UTF_8);
|
int status = resp.getStatusLine().getStatusCode();
|
||||||
requestEntity.setContentType("application/json");
|
String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8).trim();
|
||||||
httpPost.setEntity(requestEntity);
|
|
||||||
|
|
||||||
// 发送请求并处理响应
|
if (status >= 200 && status < 300) {
|
||||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
// 如果返回 JSON 对象,则解析出 token 字段
|
||||||
int statusCode = response.getStatusLine().getStatusCode();
|
if (body.startsWith("{") && body.endsWith("}")) {
|
||||||
if (statusCode >= 200 && statusCode < 300) {
|
JSONObject obj = JSONObject.parseObject(body);
|
||||||
HttpEntity respEntity = response.getEntity();
|
String token = obj.getString("token");
|
||||||
String body = EntityUtils.toString(respEntity, StandardCharsets.UTF_8);
|
log.info("登录成功,token={}", token);
|
||||||
log.info("token:{}",body);
|
return token;
|
||||||
// 返回原始返回值(假设就是 JWT token 字符串)
|
}
|
||||||
|
// 否则直接当成原始 JWT 返回
|
||||||
|
log.info("登录成功,token={}", body);
|
||||||
return body;
|
return body;
|
||||||
} else {
|
} else {
|
||||||
log.error("Login failed, HTTP status code: " + statusCode);
|
log.error("Login failed: HTTP {} - {}", status, body);
|
||||||
return "";
|
throw new IOException("Login failed: HTTP " + status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,49 +69,97 @@ public class FileBrowserUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* —— 第二步:上传文件 ——
|
* —— 第二步:上传文件 ——
|
||||||
+ * curl -v -X POST \
|
* POST {domain}/api/resources/{encodedPath}?override=true
|
||||||
+ * "$DOMAIN/api/resources/$REMOTE_PATH?override=true" \ // 服务器上相对路径
|
* Header: X-Auth: token
|
||||||
+ * -H "X-Auth: $TOKEN" \
|
|
||||||
+ * -H "Content-Type: image/jpeg" \ // 根据文件类型替换
|
|
||||||
+ * --data-binary "@/path/to/local/photo.jpg" // 以 raw body 方式上传
|
|
||||||
*/
|
*/
|
||||||
|
public String uploadFile(byte[] fileBytes, String fileName) throws IOException {
|
||||||
public String uploadAndGetUrl(byte[] fileBytes, String fileName) throws IOException {
|
|
||||||
// 1. 登录拿 token
|
|
||||||
String token = login();
|
String token = login();
|
||||||
|
|
||||||
// 2. 确定远端存储路径和编码(保留斜杠)
|
|
||||||
String remotePath = "store/" + fileName;
|
String remotePath = "store/" + fileName;
|
||||||
String encodedPath = URLEncoder.encode(remotePath, StandardCharsets.UTF_8.toString())
|
String encodedPath = URLEncoder
|
||||||
|
.encode(remotePath, StandardCharsets.UTF_8)
|
||||||
.replace("%2F", "/");
|
.replace("%2F", "/");
|
||||||
|
|
||||||
// 3. 根据文件名猜 MIME 类型,fallback 到 application/octet-stream
|
// 根据后缀猜 MIME 类型
|
||||||
String mimeType = URLConnection.guessContentTypeFromName(fileName);
|
String mimeType = URLConnection.guessContentTypeFromName(fileName);
|
||||||
if (mimeType == null) {
|
if (mimeType == null) {
|
||||||
mimeType = "application/octet-stream";
|
mimeType = "application/octet-stream";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 构造上传 URL
|
|
||||||
String uploadUrl = domain + "/api/resources/" + encodedPath + "?override=true";
|
String uploadUrl = domain + "/api/resources/" + encodedPath + "?override=true";
|
||||||
|
|
||||||
// 5. 执行 upload
|
|
||||||
try (CloseableHttpClient client = HttpClients.createDefault()) {
|
try (CloseableHttpClient client = HttpClients.createDefault()) {
|
||||||
HttpPost post = new HttpPost(uploadUrl);
|
HttpPost post = new HttpPost(uploadUrl);
|
||||||
post.setHeader("X-Auth", token);
|
post.setHeader("X-Auth", token);
|
||||||
post.setEntity(new ByteArrayEntity(fileBytes, ContentType.create(mimeType)));
|
post.setEntity(new ByteArrayEntity(fileBytes, ContentType.create(mimeType)));
|
||||||
|
|
||||||
try (CloseableHttpResponse resp = client.execute(post)) {
|
try (CloseableHttpResponse resp = client.execute(post)) {
|
||||||
int status = resp.getStatusLine().getStatusCode();
|
int status = resp.getStatusLine().getStatusCode();
|
||||||
String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
|
String respBody = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
|
||||||
if (status < 200 || status >= 300) {
|
if (status < 200 || status >= 300) {
|
||||||
log.error("文件上传失败: HTTP {},{}", status, body);
|
log.error("文件上传失败: HTTP {} - {}", status, respBody);
|
||||||
return "";
|
throw new IOException("Upload failed: HTTP " + status);
|
||||||
|
}
|
||||||
|
log.info("文件上传成功,remotePath={}", remotePath);
|
||||||
|
return remotePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 拼接 raw 下载链接并返回
|
/**
|
||||||
String downloadUrl = domain + "/api/raw/" + encodedPath;
|
* 第三步:生成公开分享链接
|
||||||
log.info("文件下载链接:{}", downloadUrl);
|
* 模拟浏览器的 POST /api/share/{encodedPath} 请求,body 为 "{}"
|
||||||
return downloadUrl;
|
*/
|
||||||
|
public String createShareLink(String remotePath) throws IOException {
|
||||||
|
String token = login();
|
||||||
|
|
||||||
|
// URL encode 并保留斜杠
|
||||||
|
String encodedPath = URLEncoder
|
||||||
|
.encode(remotePath, StandardCharsets.UTF_8)
|
||||||
|
.replace("%2F", "/");
|
||||||
|
|
||||||
|
String shareUrl = domain + "/api/share/" + encodedPath;
|
||||||
|
log.info("准备创建分享链接,POST {}", shareUrl);
|
||||||
|
|
||||||
|
try (CloseableHttpClient client = HttpClients.createDefault()) {
|
||||||
|
HttpPost post = new HttpPost(shareUrl);
|
||||||
|
post.setHeader("Cookie", "auth=" + token);
|
||||||
|
post.setHeader("X-Auth", token);
|
||||||
|
post.setHeader("Content-Type", "text/plain;charset=UTF-8");
|
||||||
|
post.setEntity(new StringEntity("{}", StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
try (CloseableHttpResponse resp = client.execute(post)) {
|
||||||
|
int status = resp.getStatusLine().getStatusCode();
|
||||||
|
String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8).trim();
|
||||||
|
|
||||||
|
if (status < 200 || status >= 300) {
|
||||||
|
log.error("创建分享失败 HTTP {} - {}", status, body);
|
||||||
|
throw new IOException("Share failed: HTTP " + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========= 这里改为先检测是对象还是数组 =========
|
||||||
|
JSONObject json;
|
||||||
|
if (body.startsWith("[")) {
|
||||||
|
// 如果真返回数组(老版本可能是 [{...}])
|
||||||
|
json = JSONArray.parseArray(body).getJSONObject(0);
|
||||||
|
} else if (body.startsWith("{")) {
|
||||||
|
// 当前版本直接返回对象
|
||||||
|
json = JSONObject.parseObject(body);
|
||||||
|
} else {
|
||||||
|
throw new IOException("Unexpected share response: " + body);
|
||||||
|
}
|
||||||
|
|
||||||
|
String hash = json.getString("hash");
|
||||||
|
String publicUrl = domain + "/api/public/dl/" + hash + "/" + remotePath;
|
||||||
|
log.info("创建分享链接成功:{}", publicUrl);
|
||||||
|
return publicUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* —— 整合示例:上传并立即返回分享链接 ——
|
||||||
|
*/
|
||||||
|
public String uploadAndGetUrl(byte[] fileBytes, String fileName) throws IOException {
|
||||||
|
String remotePath = uploadFile(fileBytes, fileName);
|
||||||
|
return createShareLink(remotePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user