OssImageDisplayUrlHelper.cs 3.71 KB
using System;
using System.IO;

namespace NCC.Extend
{
    /// <summary>
    /// 阿里云 OSS 图片访问地址:通过 x-oss-process 使用控制台「图片样式」在线缩略(不额外存文件)
    /// </summary>
    public static class OssImageDisplayUrlHelper
    {
        /// <summary>
        /// 默认样式名(与 OSS 控制台「样式规则」名称一致;可通过 NCC_App:AliyunOSS:ThumbnailStyle 覆盖)
        /// </summary>
        public const string DefaultThumbnailStyleName = "samll_img";

        /// <summary>
        /// 构建样式处理串:style/样式名
        /// </summary>
        public static string BuildStyleProcess(string styleName)
        {
            var s = string.IsNullOrWhiteSpace(styleName) ? DefaultThumbnailStyleName : styleName.Trim();
            return "style/" + s;
        }

        /// <summary>
        /// 作为 URL 查询串追加的片段(已对 value 做转义)
        /// </summary>
        /// <summary>
        /// 生成查询串,与控制台文档一致:<c>?x-oss-process=style/样式名</c>(样式名一般仅字母数字下划线,不整体编码)
        /// </summary>
        public static string ThumbnailProcessQueryForStyle(string styleName)
        {
            var s = string.IsNullOrWhiteSpace(styleName) ? DefaultThumbnailStyleName : styleName.Trim();
            foreach (var c in s)
            {
                if (!(char.IsLetterOrDigit(c) || c == '_' || c == '-' || c == '.'))
                    return "x-oss-process=" + Uri.EscapeDataString(BuildStyleProcess(styleName));
            }

            return "x-oss-process=style/" + s;
        }

        /// <summary>
        /// 是否可对扩展名做在线缩略(排除 svg 等)
        /// </summary>
        public static bool IsRasterImageExtension(string ext)
        {
            if (string.IsNullOrWhiteSpace(ext)) return false;
            var e = ext.Trim().TrimStart('.').ToLowerInvariant();
            return e is "jpg" or "jpeg" or "png" or "gif" or "webp" or "bmp" or "tiff";
        }

        /// <summary>
        /// 在图片 URL 后追加 OSS 图片处理参数,得到缩略图地址
        /// </summary>
        /// <param name="url">完整 http(s) 地址或站内 /api/File/Image/... 相对路径</param>
        /// <param name="extensionHint">扩展名提示(如 jpg),相对路径且无后缀时可传</param>
        /// <param name="ossImageStyleName">OSS 控制台图片样式名称,空则用 <see cref="DefaultThumbnailStyleName"/></param>
        public static string AppendThumbnailToUrl(string url, string extensionHint = null, string ossImageStyleName = null)
        {
            if (string.IsNullOrWhiteSpace(url)) return url;
            if (url.IndexOf("x-oss-process=", StringComparison.OrdinalIgnoreCase) >= 0)
                return url;

            var ext = extensionHint;
            if (string.IsNullOrWhiteSpace(ext))
            {
                try
                {
                    if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
                        url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
                    {
                        ext = Path.GetExtension(new Uri(url).AbsolutePath);
                    }
                }
                catch
                {
                    // ignore
                }
            }
            if (string.IsNullOrWhiteSpace(ext))
                ext = Path.GetExtension(url.Split('?', '#')[0]);
            if (!IsRasterImageExtension(ext))
                return url;

            var q = ThumbnailProcessQueryForStyle(ossImageStyleName);
            return url.Contains("?", StringComparison.Ordinal) ? $"{url}&{q}" : $"{url}?{q}";
        }
    }
}