Newer
Older
Sakayaki / Pages / Folder.cshtml.cs
@fabre fabre 9 hours ago 4 KB 超级缩略图
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore;
using Sakayaki.Models;
using Sakayaki.Services;

namespace Sakayaki.Pages;

public class FolderModel(AppDbContext dbContext, ThumbnailService thumbnailService, IConfiguration configuration) : PageModel
{
    private static readonly HashSet<string> ImageExtensions = new(StringComparer.OrdinalIgnoreCase)
    {
        ".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp"
    };

    private readonly AppDbContext _dbContext = dbContext;
    private readonly ThumbnailService _thumbnailService = thumbnailService;
    private readonly string? _rootPath = configuration["Fanbox:RootPath"];

    public sealed record ImageItem(string FileName, string Url, string ThumbnailUrl);

    public FanboxFolder? Folder { get; private set; }
    public IReadOnlyList<string> Keywords { get; private set; } = Array.Empty<string>();
    public IReadOnlyList<ImageItem> Images { get; private set; } = Array.Empty<ImageItem>();
    public string? Keyword { get; private set; }
    public int? ReturnPage { get; private set; }

    public string BackUrl =>
        Url.Page("Index", null, new { keyword = Keyword, p = ReturnPage }) ?? Url.Page("Index") ?? "/";

    public async Task OnGetAsync(Guid id, string? keyword, int? p)
    {
        Keyword = string.IsNullOrWhiteSpace(keyword) ? null : keyword.Trim();
        ReturnPage = p is > 1 ? p : null;

        Folder = await _dbContext.FanboxFolders.AsNoTracking()
            .FirstOrDefaultAsync(x => x.Id == id);
        if (Folder is null)
        {
            return;
        }

        if (!string.IsNullOrWhiteSpace(Folder.Keywords))
        {
            Keywords = Folder.Keywords
                .Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
                .ToArray();
        }

        if (string.IsNullOrWhiteSpace(_rootPath))
        {
            return;
        }

        var folderPath = Path.Combine(_rootPath, Folder.FolderName);
        if (!Directory.Exists(folderPath))
        {
            return;
        }

        var images = Directory.GetFiles(folderPath)
            .Where(path => ImageExtensions.Contains(Path.GetExtension(path)))
            .OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
            .Select(path =>
            {
                var fileName = Path.GetFileName(path);
                var url = Url.Page("Folder", "Image", new { id = Folder.Id, file = fileName }) ?? string.Empty;
                var thumbnailUrl = Url.Page("Folder", "Thumbnail", new { id = Folder.Id, file = fileName }) ?? string.Empty;
                return new ImageItem(fileName, url, thumbnailUrl);
            })
            .ToArray();

        Images = images;
    }

    public async Task<IActionResult> OnGetImageAsync(Guid id, string file)
    {
        if (string.IsNullOrWhiteSpace(_rootPath) || string.IsNullOrWhiteSpace(file))
            return NotFound();

        var safeFile = Path.GetFileName(file);
        if (string.IsNullOrWhiteSpace(safeFile))
            return NotFound();
        if (!ImageExtensions.Contains(Path.GetExtension(safeFile)))
            return NotFound();

        var folder = await _dbContext.FanboxFolders.AsNoTracking()
            .FirstOrDefaultAsync(x => x.Id == id);
        if (folder is null)
            return NotFound();

        var filePath = Path.Combine(_rootPath, folder.FolderName, safeFile);
        if (!System.IO.File.Exists(filePath))
            return NotFound();

        var provider = new FileExtensionContentTypeProvider();
        if (!provider.TryGetContentType(filePath, out var contentType))
            contentType = "application/octet-stream";

        return PhysicalFile(filePath, contentType);
    }

    public async Task<IActionResult> OnGetThumbnailAsync(Guid id, string file)
    {
        if (string.IsNullOrWhiteSpace(file))
            return NotFound();

        var safeFile = Path.GetFileName(file);
        if (string.IsNullOrWhiteSpace(safeFile))
            return NotFound();
        if (!ImageExtensions.Contains(Path.GetExtension(safeFile)))
            return NotFound();

        var folder = await _dbContext.FanboxFolders.AsNoTracking()
            .FirstOrDefaultAsync(x => x.Id == id);
        if (folder is null)
            return NotFound();

        var thumbPath = _thumbnailService.GetThumbnailPath(folder.Id, safeFile);
        if (string.IsNullOrWhiteSpace(thumbPath))
            return NotFound();

        if (!System.IO.File.Exists(thumbPath))
        {
            await _thumbnailService.EnsureThumbnailAsync(folder.Id, folder.FolderName, safeFile, HttpContext.RequestAborted);
        }

        if (!System.IO.File.Exists(thumbPath))
            return NotFound();

        return PhysicalFile(thumbPath, "image/jpeg");
    }
}