【LSky Pro 改造】升级图片查询接口 & 上传支持指定相册

【LSky Pro 改造】升级图片查询接口 & 上传支持指定相册

|

最近继续折腾 LSky Pro 图床,发现原有的图片查询接口有点"死板",只能按相册查,不能按存储策略查,也不能跨相册获取所有图片。于是又开始了一番改造 😏

这篇文章记录了我对 LSky Pro 图片接口的两个主要改进:

1. 升级图片查询接口,支持按存储策略灵活查询

2. 上传接口新增指定相册功能

📝 问题分析

原有的 /api/v1/images 接口存在以下限制:

问题 说明
无法获取所有图片 不传 album_id 只返回未分类图片,无法跨相册查询
无法按存储策略筛选 没有 strategy_id 参数支持
分页数量固定 写死 40 条,无法通过参数调整

这显然不够灵活,所以决定动手改一改 🔧

🛠️ 实现步骤

1. 新建控制器

在路径 app/Http/Controllers/Api/V1/ 下新建控制器Images2Controller.php

核心代码如下:

<?php

namespace App\Http\Controllers\Api\V1;

use App\Enums\ImagePermission;
use App\Http\Controllers\Controller;
use App\Models\Image;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;

/**
 * 图片列表接口 v2
 * 20251214 by Tim 新增:支持按存储策略、相册灵活查询图片
 */
class Images2Controller extends Controller
{
    /**
     * 获取图片列表
     *
     * 参数组合逻辑:
     * - 无参数:返回默认相册(未分类)的图片
     * - album_id=5:返回相册5的所有图片
     * - strategy_id=1:返回存储策略1的所有图片(跨相册)
     * - strategy_id=1&album_id=5:返回存储策略1且相册5的图片
     */
    public function index(Request $request): Response
    {
        /** @var User $user */
        $user = Auth::user();

        $strategyId = (int) $request->query('strategy_id');
        $albumId = (int) $request->query('album_id');
        $perPage = (int) $request->query('per_page') ?: 40;

        // 验证用户是否有权限访问该存储策略
        if ($strategyId) {
            $hasAccess = $user->group->strategies()->where('strategies.id', $strategyId)->exists();
            if (! $hasAccess) {
                return $this->fail('无权访问该存储策略');
            }
        }
        
        // 语法:->when(条件,true回调,false回调)
        $images = $user->images()
            // 按存储策略过滤
            ->when($strategyId, function (Builder $builder, $strategyId) {
                $builder->where('strategy_id', $strategyId);
            })
            // 相册过滤
            ->when($albumId, function (Builder $builder, $albumId) {
                // 有相册ID,按相册过滤
                $builder->where('album_id', $albumId);
            }, function (Builder $builder) use ($strategyId) {
                // 没有相册ID时:有存储策略则不限制相册,没有存储策略则返回默认相册(未分类)
                if (!$strategyId) {
                    $builder->whereNull('album_id');
                }
            })
            // 排序
            ->when($request->query('order') ?: 'newest', function (Builder $builder, $order) {
                switch ($order) {
                    case 'earliest':
                        $builder->orderBy('created_at');
                        break;
                    case 'utmost':
                        $builder->orderByDesc('size');
                        break;
                    case 'least':
                        $builder->orderBy('size');
                        break;
                    default:
                        $builder->latest();
                }
            })
            // 权限过滤
            ->when($request->query('permission') ?: 'all', function (Builder $builder, $permission) {
                switch ($permission) {
                    case 'public':
                        $builder->where('permission', ImagePermission::Public);
                        break;
                    case 'private':
                        $builder->where('permission', ImagePermission::Private);
                        break;
                }
            })
            // 关键词搜索
            ->when($request->query('keyword'), function (Builder $builder, $keyword) {
                $builder->where(function (Builder $query) use ($keyword) {
                    $query->where('origin_name', 'like', "%{$keyword}%")
                          ->orWhere('alias_name', 'like', "%{$keyword}%");
                });
            })
            ->paginate($perPage)
            ->withQueryString();

        // 处理返回字段,与 images 接口保持一致
        $images->getCollection()->each(function (Image $image) {
            $image->human_date = $image->created_at->diffForHumans();
            $image->date = $image->created_at->format('Y-m-d H:i:s');
            $image->append(['pathname', 'links'])->setVisible([
                'album', 'key', 'name', 'pathname', 'origin_name', 'size', 'mimetype', 'extension', 'md5', 'sha1',
                'width', 'height', 'links', 'human_date', 'date',
            ]);
        });

        return $this->success('success', $images);
    }
}

2. 配置路由

编辑 routes/api.php,用新控制器替换原有路由:

<?php

use App\Http\Controllers\Api\V1\Images2Controller; //20251214 by Tim

Route::group([

    'middleware' => 'auth:sanctum',

], function () {

// [!code --]
        Route::get('images', [ImageController::class, 'images']);
// [!code ++]
        Route::get('images', [Images2Controller::class, 'index']); //20251214 by Tim 新增图片获取,支持按存储策略、相册查询        
    // ...其他路由

});

3. 参数组合逻辑

新接口的查询逻辑:

参数组合 返回结果
无参数 默认相册(未分类)的图片
album_id=5 相册5的所有图片
strategy_id=1 存储策略1的所有图片(跨相册) ✨
strategy_id=1&album_id=5 存储策略1 且 相册5 的图片

这样就能灵活地按存储策略或相册来查询图片了 🎉

📤 上传支持指定相册

顺便把上传接口也改进了一下,支持在上传时直接指定相册。

修改文件

编辑 app/Services/ImageService.php,在 public function store(Request $request): Image 函数中添加相册参数支持:

        // 默认储存策略
        if (! is_null($user)) {
            if (Utils::config(ConfigKey::IsUserNeedVerify) && ! $user->email_verified_at) {
                throw new UploadException('账户未验证');
            }

            if ($user->status !== UserStatus::Normal) {
                throw new UploadException('账号状态异常');
            }

            if ($file->getSize() / 1024 + $user->images()->sum('size') > $user->capacity) {
                throw new UploadException('储存空间不足');
            }
// [!code ++:9]
            // 20251213 by Tim 新增API上传支持指定相册。确定图片所属相册:优先使用请求指定的相册,否则使用用户默认相册 
            $albumId = $request->has('album_id')
                ? $request->input('album_id')
                : $user->configs->get(UserConfigKey::DefaultAlbum);

            if ($albumId) {
                if (!$user->albums()->where('id', $albumId)->exists()) {
                    throw new UploadException('指定的相册不存在或不属于您');
                }
                $image->album_id = $albumId;
            }

            $image->user_id = $user->id;
            // 用户设置的图片默认权限
            $image->permission = $user->configs->get(UserConfigKey::DefaultPermission, ImagePermission::Private);
        }

使用方式

POST /api/v1/upload

Authorization: Bearer {token}

Content-Type: multipart/form-data

file: (图片文件)

album_id: 1        # 可选,指定相册ID

strategy_id: 1     # 可选,指定存储策略ID

参数说明

参数 行为
不传 album_id 使用用户默认相册设置
album_id=1 保存到指定相册(需属于当前用户)
album_id= (空值) 不保存到任何相册(覆盖默认设置)

🩹 顺手修复图片格式排除

在折腾的过程中,发现一些特殊格式(PSD、TIF、BMP 等)在处理时会出问题,于是顺手加了格式排除(虽然我目前用不上)。

格式排除汇总

处理环节 排除格式
图片处理(压缩/水印) ico, gif, svg, psd, tif, bmp, mp4, mov, avi, mkv, webm
图片检测(审核) psd, ico, tif, bmp, svg, mp4, mov, avi, mkv, webm
缩略图生成 svg, psd, tif, bmp, ico, mp4, mov, avi, mkv, webm

🧪 使用示例

查询图片

# 获取默认相册(未分类)图片

curl -H "Authorization: Bearer your_token" \

     "https://your-lsky.com/api/v1/images"

# 获取存储策略1的所有图片(跨相册)

curl -H "Authorization: Bearer your_token" \

     "https://your-lsky.com/api/v1/images?strategy_id=1"

# 获取存储策略1下相册5的图片,每页20条

curl -H "Authorization: Bearer your_token" \

     "https://your-lsky.com/api/v1/images?strategy_id=1&album_id=5&per_page=20"

上传到指定相册

curl -X POST \

     -H "Authorization: Bearer your_token" \

     -F "file=@photo.jpg" \

     -F "album_id=3" \

     "https://your-lsky.com/api/v1/upload"

✅ 总结

本次改造完成了以下功能:

功能 说明
图片查询升级 支持按存储策略查询,支持自定义分页
上传指定相册 上传时可直接指定目标相册
格式排除优化 特殊格式不再报错

修改文件汇总

文件 修改类型 说明
app/Http/Controllers/Api/V1/Images2Controller.php 新建 图片查询接口 v2
app/Services/ImageService.php 修改 添加相册参数支持、格式排除
routes/api.php 修改 更新路由配置

完全向后兼容,原有调用方式不受影响,只是多了新功能 ~

补充

同时也对 halo-lsky-pro 插件进行升级,适配 Halo 2.22 并且上传支持相册

https://github.com/Tim0x0/halo-lsky-prohttps://github.com/Tim0x0/lsky-pro

智能创造跨越:AI工具助我“零基础”开发Halo插件的探索之旅! 2025-12-31

评论区

© 2025 Tim's Blog