Files
Foxel/Api/PictureController.cs

366 lines
12 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Foxel.Models;
using Foxel.Models.DataBase;
using Foxel.Models.Request.Picture;
using Foxel.Models.Response.Picture;
using System.Text.Json;
using System.Text.Json.Serialization;
using Foxel.Services.Media;
using Foxel.Services.Storage;
using System.IO;
using Foxel.Services.Attributes;
namespace Foxel.Controllers;
[Authorize]
[Route("api/picture")]
public class PictureController(IPictureService pictureService, IStorageService storageService) : BaseApiController
{
[HttpGet("get_pictures")]
public async Task<ActionResult<PaginatedResult<PictureResponse>>> GetPictures(
[FromQuery] FilteredPicturesRequest request)
{
try
{
List<string>? tagsList = null;
if (!string.IsNullOrWhiteSpace(request.Tags))
{
tagsList = request.Tags.Split(',')
.Select(t => t.Trim())
.Where(t => !string.IsNullOrWhiteSpace(t))
.ToList();
}
var currentUserId = GetCurrentUserId();
var result = await pictureService.GetPicturesAsync(
request.Page,
request.PageSize,
request.SearchQuery,
tagsList,
request.StartDate,
request.EndDate,
currentUserId,
request.SortBy,
request.OnlyWithGps,
request.UseVectorSearch,
request.SimilarityThreshold,
request.ExcludeAlbumId,
request.AlbumId,
request.OnlyFavorites,
request.OwnerId,
request.IncludeAllPublic
);
return PaginatedSuccess(result.Data, result.TotalCount, result.Page, result.PageSize);
}
catch (Exception ex)
{
return PaginatedError<PictureResponse>($"获取图片失败: {ex.Message}", 500);
}
}
[AllowAnonymous]
[HttpPost("upload_picture")]
[Consumes("multipart/form-data")]
public async Task<ActionResult<BaseResult<PictureResponse>>> UploadPicture(
[FromForm] UploadPictureRequest request)
{
if (request.File.Length == 0)
return Error<PictureResponse>("没有上传文件");
try
{
var userId = GetCurrentUserId();
await using var stream = request.File.OpenReadStream();
var result = await pictureService.UploadPictureAsync(
request.File.FileName,
stream,
request.File.ContentType,
userId,
(PermissionType)request.Permission!,
request.AlbumId,
request.StorageType
);
var picture = result.Picture;
return Success(picture, "图片上传成功");
}
catch (KeyNotFoundException ex)
{
return Error<PictureResponse>(ex.Message, 404);
}
catch (Exception ex)
{
return Error<PictureResponse>($"上传图片失败: {ex.Message}", 500);
}
}
[HttpPost("delete_pictures")]
public async Task<ActionResult<BaseResult<object>>> DeleteMultiplePictures(
[FromBody] DeleteMultiplePicturesRequest request)
{
try
{
var currentUserId = GetCurrentUserId();
if (currentUserId == null)
return Error<object>("无法识别用户信息");
if (!request.PictureIds.Any())
return Error<object>("未提供要删除的图片ID");
// 获取删除结果
var results = await pictureService.DeleteMultiplePicturesAsync(request.PictureIds);
// 权限验证和处理结果
var unauthorizedIds = new List<int>();
var notFoundIds = new List<int>();
var successIds = new List<int>();
var errors = new Dictionary<int, string>();
foreach (var (pictureId, (success, errorMessage, ownerId)) in results)
{
// 检查权限
if (ownerId.HasValue && ownerId.Value != currentUserId.Value)
{
unauthorizedIds.Add(pictureId);
continue;
}
if (!success)
{
notFoundIds.Add(pictureId);
}
else if (!string.IsNullOrEmpty(errorMessage))
{
errors[pictureId] = errorMessage;
}
else
{
successIds.Add(pictureId);
}
}
// 如果有未授权或其他错误,返回适当的响应
if (unauthorizedIds.Any() || notFoundIds.Any() || errors.Any())
{
var messages = new List<string>();
if (unauthorizedIds.Any())
messages.Add($"无权删除以下图片: {string.Join(", ", unauthorizedIds)}");
if (notFoundIds.Any())
messages.Add($"找不到以下图片: {string.Join(", ", notFoundIds)}");
if (errors.Any())
messages.Add(string.Join("; ", errors.Select(e => $"图片ID {e.Key}: {e.Value}")));
return StatusCode(207, new BaseResult<object>
{
Success = successIds.Any(),
Message = string.Join("; ", messages),
StatusCode = 207,
Data = new
{
SuccessCount = successIds.Count,
SuccessIds = successIds,
UnauthorizedIds = unauthorizedIds,
NotFoundIds = notFoundIds,
Errors = errors
}
});
}
return Success<object>($"成功删除 {successIds.Count} 张图片");
}
catch (Exception ex)
{
return Error<object>($"删除图片失败: {ex.Message}", 500);
}
}
[HttpPost("update_picture")]
public async Task<ActionResult<BaseResult<PictureResponse>>> UpdatePicture(
[FromBody] UpdatePictureRequestWithId request)
{
try
{
var currentUserId = GetCurrentUserId();
if (currentUserId == null)
return Error<PictureResponse>("无法识别用户信息");
var (picture, ownerId) = await pictureService.UpdatePictureAsync(
request.Id, request.Name, request.Description, request.Tags);
// 权限验证
if (ownerId.HasValue && ownerId.Value != currentUserId.Value)
{
return Error<PictureResponse>("您没有权限更新此图片", 403);
}
return Success(picture, "图片信息已成功更新");
}
catch (KeyNotFoundException)
{
return Error<PictureResponse>("找不到要更新的图片", 404);
}
catch (Exception ex)
{
return Error<PictureResponse>($"更新图片失败: {ex.Message}", 500);
}
}
[HttpPost("favorite")]
public async Task<ActionResult<BaseResult<bool>>> FavoritePicture([FromBody] FavoriteRequest request)
{
try
{
var userId = GetCurrentUserId();
if (userId == null)
return Error<bool>("无法识别用户信息", 401);
var result = await pictureService.FavoritePictureAsync(request.PictureId, userId.Value);
return Success(result, "图片收藏成功");
}
catch (KeyNotFoundException)
{
return Error<bool>("找不到指定图片", 404);
}
catch (InvalidOperationException ex)
{
return Error<bool>(ex.Message);
}
catch (Exception ex)
{
return Error<bool>($"收藏图片失败: {ex.Message}", 500);
}
}
[HttpPost("unfavorite")]
public async Task<ActionResult<BaseResult<bool>>> UnfavoritePicture([FromBody] FavoriteRequest request)
{
try
{
var userId = GetCurrentUserId();
if (userId == null)
return Error<bool>("无法识别用户信息", 401);
var result = await pictureService.UnfavoritePictureAsync(request.PictureId, userId.Value);
return Success(result, "已取消收藏");
}
catch (KeyNotFoundException)
{
return Error<bool>("找不到指定图片或收藏记录", 404);
}
catch (Exception ex)
{
return Error<bool>($"取消收藏失败: {ex.Message}", 500);
}
}
[HttpGet("get_telegram_file")]
[AllowAnonymous]
public async Task<IActionResult> GetTelegramFile([FromQuery] string fileId)
{
try
{
// 创建一个模拟的存储元数据
var metadata = new
{
FileId = fileId,
OriginalFileName = "telegram_file"
};
// 序列化为 JSON 字符串,与 TelegramStorageProvider 中的格式保持一致
string storagePath = JsonSerializer.Serialize(metadata);
try
{
// 使用 storageService 下载文件,这样会自动使用配置的代理
string tempFilePath = await storageService.ExecuteAsync(StorageType.Telegram,
provider => provider.DownloadFileAsync(storagePath));
// 获取文件内容类型
string contentType = GetContentTypeFromPath(tempFilePath);
// 返回文件
return PhysicalFile(tempFilePath, contentType, Path.GetFileName(tempFilePath));
}
catch (Exception ex)
{
return StatusCode(500, $"下载 Telegram 文件失败: {ex.Message}");
}
}
catch (Exception ex)
{
return StatusCode(500, $"代理获取文件失败: {ex.Message}");
}
}
// 用于解析 Telegram getFile API 响应的辅助类
private class TelegramGetFileResponse
{
[JsonPropertyName("ok")]
public bool Ok { get; set; }
[JsonPropertyName("result")]
public TelegramFileResult? Result { get; set; }
}
private class TelegramFileResult
{
[JsonPropertyName("file_path")]
public string? FilePath { get; set; }
}
[HttpGet("proxy")]
[AllowAnonymous]
public async Task<IActionResult> GetWebDavFile([FromQuery] string path)
{
try
{
if (string.IsNullOrEmpty(path))
{
return BadRequest("文件路径不能为空");
}
// 下载文件到临时位置
string filePath = await storageService.ExecuteAsync(StorageType.WebDAV,
provider => provider.DownloadFileAsync(path));
// 确定内容类型
string contentType = GetContentTypeFromPath(path);
// 返回文件内容
return PhysicalFile(filePath, contentType, Path.GetFileName(path));
}
catch (Exception ex)
{
return StatusCode(500, $"代理获取WebDAV文件失败: {ex.Message}");
}
}
private string GetContentTypeFromPath(string path)
{
string extension = Path.GetExtension(path).ToLowerInvariant();
return extension switch
{
".jpg" or ".jpeg" => "image/jpeg",
".png" => "image/png",
".gif" => "image/gif",
".bmp" => "image/bmp",
".webp" => "image/webp",
".svg" => "image/svg+xml",
".mp4" => "video/mp4",
".avi" => "video/x-msvideo",
".mov" => "video/quicktime",
".pdf" => "application/pdf",
".doc" or ".docx" => "application/msword",
".xls" or ".xlsx" => "application/vnd.ms-excel",
_ => "application/octet-stream"
};
}
}