已编译程序下载
此处内容需要评论回复后方可阅读

使用兰空图床LskyPro+,支持的储存方案,支持自建的只有Webdav和S3兼容,如果想做一个聚合图床,最好的办法就是用Webdav。
而像Github等能够部署到Openlist(Alist)然后外显为Webdav管理,可一些只支持API或者没有API的图床怎么办?
可以试试我搭建的图床,支持SM.MS,OOXX,以及国内CDN与对象储存等多种储存方案:LHL‘s Image
SM.MS
SM.MS有公开的API,思路就是写一个Webdav,其中GET、PUT、PROPFIND等请求(Webdav)就可以把API相关内容写进去。sm.ms返回的链接写入数据库,然后就有
原文件名-SM.MS关系的记录。我如果访问http://webdav-ip:port/原文件名就会302跳转到sm.ms的url。可是存在一个问题,Webdav是不支持调回的,也就是我自建的图床并不知道sm.ms的url是多少,如果图片访问过程中多出了一个环节,需要先访问webdav然后再跳转到webdav
于是我写了一个/api/get-url?path=/原图片名的接口,用来获取sm.ms的url(这里由于图床写入数据库时候使用的cleanPath而只会返回cleanPath)
当然需要修改一下图床的源码。在上传图片写入图床数据库之前,对于特定的储存ID在写入path的时候,调用api接口替换原文件名。
请注意项目+版本号,如果需要其他项目或者特定版本的代码可以在评论区回复
LskyPro+ 2.2.3 /app/Services/PhotoService.php /**
* 储存图片信息
*
* @param array $data
* @param array $albums
* @param array<string> $tags
* @return Photo
* @throws Throwable
*/
public function store(array $data, array $albums = [], array $tags = []): Photo
{
return DB::transaction(function () use ($data, $albums, $tags) {
$originalPathname = $data['pathname'] ?? '';
$storageId = $data['storage_id'] ?? null;
$finalPathname = $originalPathname;
$externalUrl = null;
// 🔑 直接从 .env 读取 PHOTO_STORAGE_{ID}_GET_URL_API
if ($storageId && is_numeric($storageId) && $originalPathname !== '') {
$envKey = 'PHOTO_STORAGE_' . $storageId . '_GET_URL_API';
$getUrlApi = env($envKey);
if ($getUrlApi) {
try {
$fullApiUrl = rtrim($getUrlApi, '/') . '?path=' . urlencode($originalPathname);
$response = Http::timeout(5)->get($fullApiUrl);
if (
$response->successful() &&
is_array($body = $response->json()) &&
!empty($body['url'])
) {
// ✅ 直接使用 clean path(如 "i/AbC123.png")
$finalPathname = ltrim((string) $body['url'], '/');
}
} catch (\Exception $e) {
Log::warning('Failed to fetch CDN path from get_url_api', [
'storage_id' => $storageId,
'pathname' => $originalPathname,
'env_key' => $envKey,
'error' => $e->getMessage(),
]);
}
}
}
// 更新 data
$data['pathname'] = $finalPathname;
if ($externalUrl) {
$data['external_url'] = $externalUrl;
}
// 正常存储
$attributes = Arr::only($data, ['user_id', 'storage_id', 'md5', 'sha1', 'pathname']);
/** @var Photo $photo */
$photo = Photo::firstOrCreate($attributes, $data);
// 相册
if (count($albums) > 0) {
$photo->albums()->syncWithoutDetaching($albums);
}
// 标
if ($tags) {
$attach = [];
foreach ($tags as $name) {
$tag = Tag::firstOrCreate(['name' => $name]);
$attach[$tag->id] = ['user_id' => $photo->user_id];
}
$photo->tags()->syncWithoutDetaching($attach);
}
return $photo;
});
}然后在/.env中写入设置
// 将[StorageID]更改为SM.MS对于的Storage ID,WEBDAV_IP和PORT更换为对应的IP和端口,当然可以使用NGINX等反代从而支持TLS
PHOTO_STORAGE_[StorageID]_GET_URL_API=http://WEBDAV_IP:PORT/api/get-url图床WebDAV配置设置:
储存目录: /
访问URL: https://i.loli.net (如果是SM.MS Premium,修改为https://cdn.sa.net)
访问前缀: 不用填写
命名填写: 建议只写 {md5}st=>start: 开始
e=>end: 结束
op1=>operation: Lsky Pro+ / WebDAV 客户端 PUT 请求上传文件
op2=>operation: WebDAV 代理服务接收请求并调用 SM.MS API 上传文件
op3=>operation: SM.MS 图床返回 CDN URL 和删除链接
op4=>operation: WebDAV 代理服务写入映射记录 (原文件名.png ↔ AbC123.png)
op5=>operation: 准备写入数据库前,调用 /api/get-url?path=原文件名.png 获取URL
op6=>operation: 查询 SQLite 中的映射记录
op7=>operation: 返回 URL { "url": "AbC123.png" }
op8=>operation: 存入 pathname = AbC123.png 到 Lsky 数据库
op9=>operation: 用户访问图片 GET https://i.loli.net/AbC123.png
op10=>operation: 直接访问 WebDAV 路径 GET /原文件名.png
op11=>operation: WebDAV 代理服务进行 302 重定向到 https://i.loli.net/AbC123.png
st->op1->op2->op3->op4->op5->op6->op7->op8->e
op9->e
op10->op11->eOOXX.OOO
由于ooxx没有公开API接口,所以我们需要自己分析一下。上传图片可以发现接口是
/upload,但是细看发现上传的时候消息头中有个cookie:_xsrf=2|d8c52d8d|bb375f17f30175601ddd6f0ceebd0d94|1766648051 
所以我们需要写一个getValidXSRF():
- 发起 GET 请求到 https://ooxx.ooo/upload
- 从响应 Cookie 中提取 _xsrf
- 将 _xsrf 作为字段和 Cookie 同时发送(模拟浏览器行为)
再分析响应:
[{"slug": "NWMwN", "ext": ".png", "filename": "\u622a\u56fe 2025-12-27 21-08-23.png", "delete_token": "8GpOekI_6Wc", "url": "https://i.ooxx.ooo/i/NWMwN.html", "image_url": "https://i.ooxx.ooo/i/NWMwN.png", "preview_url": "http://ooxx.ooo/i/NWMwN.png", "delete_url": "http://ooxx.ooo/i/delete/NWMwN/8GpOekI_6Wc"}]那么接下来一样的,建立数据库然后将请求相应的内容写入。
项目部署
由于ooxx webdav这个代码写的比较仓促,而且这个没有api token,那么项目配置只有端口和webdav密码,所以我就写死在代码里面了,默认用户admin,密码123456,需要在编译前在项目中修改。
git clone https://github.com/yourname/ooxx-webdav.git
cd ooxx-webdav找到这一行并修改
if !ok || username != "[email protected]" || password != "YourSecurePassword!" {编译后运行
go build -o ooxx-webdav .
chmod ./ooxx-webdav
./ooxx-webdav 采用 CC BY-NC-SA 4.0 协议授权,转载请注明来源。
沙发~ 很实用哦
@(太开心)