1、增加图片视频
2、优化代码结构 3、up主下载分类逻辑可配置
This commit is contained in:
@@ -1,69 +0,0 @@
|
||||
namespace dy.net.utils
|
||||
{
|
||||
public static class CookieValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// 粗略验证Cookie字符串的格式是否合法
|
||||
/// 核心校验规则:
|
||||
/// 1. 不为空或纯空白字符串
|
||||
/// 2. 由分号分隔的键值对组成(键值对格式为 key=value,key不能为空)
|
||||
/// 3. 键名(key)不包含非法字符(分号、逗号、空格、等号)
|
||||
/// 注:此方法为粗略验证,不严格遵循RFC 6265标准(如未校验控制字符、长度限制等)
|
||||
/// </summary>
|
||||
/// <param name="cookieStr">待验证的Cookie字符串</param>
|
||||
/// <returns>格式是否合法(true=合法,false=非法)</returns>
|
||||
public static bool IsRoughlyValidCookieFormat(string cookieStr)
|
||||
{
|
||||
// 规则1:Cookie字符串不能为空或纯空白
|
||||
if (string.IsNullOrWhiteSpace(cookieStr))
|
||||
{
|
||||
Serilog.Log.Error("验证失败:Cookie字符串为空或纯空白");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 按分号分隔Cookie项(处理项前后的空格,过滤空项)
|
||||
var cookieItems = cookieStr.Split(';')
|
||||
.Select(item => item.Trim()) // 去除项前后空格(如 "a=b; c=d" → ["a=b", "c=d"])
|
||||
.Where(item => !string.IsNullOrWhiteSpace(item)) // 过滤空项(如末尾分号导致的空项)
|
||||
.ToList();
|
||||
|
||||
// 若分割后无有效项(如全是分号或空格)
|
||||
if (!cookieItems.Any())
|
||||
{
|
||||
Serilog.Log.Error("验证失败:Cookie字符串仅包含分隔符或空格");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 定义键名(key)的非法字符(粗略验证,选取最常见的非法字符)
|
||||
char[] invalidKeyChars = { ';', ',', ' ', '=' };
|
||||
|
||||
// 遍历每个Cookie项,验证键值对格式
|
||||
foreach (var item in cookieItems)
|
||||
{
|
||||
// 规则2:每个项必须包含等号(=),且等号不能是第一个字符(保证key非空)
|
||||
int equalsIndex = item.IndexOf('=');
|
||||
if (equalsIndex <= 0) // equalsIndex=0 → 以等号开头(key为空);equalsIndex=-1 → 无等号
|
||||
{
|
||||
Serilog.Log.Error($"验证失败:Cookie项 [{item}] 缺少等号或键名为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 提取键名(key)并验证
|
||||
string key = item.Substring(0, equalsIndex).Trim(); // 再次Trim以防key前后有空格(如 " key =value")
|
||||
|
||||
// 规则3:键名不能包含非法字符
|
||||
if (key.Any(c => invalidKeyChars.Contains(c)))
|
||||
{
|
||||
Serilog.Log.Error($"验证失败:Cookie项 [{item}] 的键名 [{key}] 包含非法字符(; , 空格 =)");
|
||||
return false;
|
||||
}
|
||||
|
||||
// (可选)粗略验证value:此处不做严格限制,允许value为空(如 "key=")或包含特殊字符
|
||||
}
|
||||
|
||||
// 所有项均通过验证
|
||||
//Serilog.Log.Error("Cookie格式粗略验证通过");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace dy.net.utils
|
||||
{
|
||||
|
||||
|
||||
public static class DiskInfoHelper
|
||||
{/// <summary>
|
||||
/// 在Docker容器中获取Linux宿主机的本地固定磁盘总空间(GB)
|
||||
/// 前提:宿主机需挂载 /proc 到容器内的 /host/proc(启动时加 -v /proc:/host/proc:ro)
|
||||
/// </summary>
|
||||
/// <returns>总空间字符串(如 "1408.35 GB"),失败时返回错误信息</returns>
|
||||
public static string GetDockerHostTotalDiskSpaceGB()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 检查是否为Linux宿主机(Docker主要运行在Linux上)
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
return "仅支持Linux宿主机(Docker容器内)";
|
||||
}
|
||||
|
||||
// 2. 检查宿主机/proc是否已挂载到容器
|
||||
string hostProcMountsPath = "/app/db/mounts";
|
||||
if (!File.Exists(hostProcMountsPath))
|
||||
{
|
||||
return "未检测到宿主机/proc挂载,请使用 -v /proc:/host/proc:ro 启动容器";
|
||||
}
|
||||
|
||||
// 3. 从宿主机/proc/mounts筛选物理磁盘挂载点(排除虚拟文件系统)
|
||||
var physicalMounts = GetHostPhysicalMounts(hostProcMountsPath);
|
||||
if (!physicalMounts.Any())
|
||||
{
|
||||
return "未找到宿主机的物理磁盘挂载点";
|
||||
}
|
||||
|
||||
// 4. 计算所有物理磁盘的总空间(通过df命令获取挂载点的总空间)
|
||||
long totalBytes = 0;
|
||||
foreach (var mountPoint in physicalMounts)
|
||||
{
|
||||
// 执行df命令获取宿主机挂载点的总空间(需容器内有df工具,或通过/proc/diskstats计算)
|
||||
var (success, bytes) = GetMountPointTotalBytes(mountPoint);
|
||||
if (success)
|
||||
{
|
||||
totalBytes += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalBytes == 0)
|
||||
{
|
||||
return "无法读取宿主机磁盘空间(可能权限不足)";
|
||||
}
|
||||
|
||||
// 5. 转换为GB
|
||||
return ConvertBytesToGb(totalBytes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"获取宿主机磁盘空间失败:{ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从宿主机/proc/mounts筛选物理磁盘挂载点(排除虚拟文件系统)
|
||||
/// </summary>
|
||||
private static List<string> GetHostPhysicalMounts(string hostProcMountsPath)
|
||||
{
|
||||
var physicalMounts = new List<string>();
|
||||
// 虚拟文件系统类型(排除这些类型,剩下的视为物理磁盘相关)
|
||||
var virtualFsTypes = new HashSet<string>
|
||||
{
|
||||
"tmpfs", "sysfs", "proc", "devtmpfs", "devpts", "cgroup", "cgroup2",
|
||||
"securityfs", "pstore", "debugfs", "hugetlbfs", "mqueue", "configfs",
|
||||
"fusectl", "overlay", "squashfs", "overlay2" // overlay/overlay2是Docker自身的存储驱动,需排除
|
||||
};
|
||||
|
||||
foreach (var line in File.ReadAllLines(hostProcMountsPath))
|
||||
{
|
||||
var parts = line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length >= 3)
|
||||
{
|
||||
string device = parts[0]; // 设备名(如/dev/sda1)
|
||||
string mountPoint = parts[1]; // 挂载点(如/)
|
||||
string fsType = parts[2]; // 文件系统类型
|
||||
|
||||
// 筛选条件:非虚拟文件系统 + 设备名以/dev/开头(物理设备)
|
||||
if (!virtualFsTypes.Contains(fsType) && device.StartsWith("/dev/"))
|
||||
{
|
||||
physicalMounts.Add(mountPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return physicalMounts.Distinct().ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过df命令获取宿主机挂载点的总空间(字节)
|
||||
/// (需容器内安装coreutils,或替换为解析/proc/diskstats的逻辑)
|
||||
/// </summary>
|
||||
private static (bool success, long totalBytes) GetMountPointTotalBytes(string hostMountPoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 在容器内执行df命令,指定宿主机的挂载点(需宿主机路径在容器内可见,或通过/proc计算)
|
||||
// 注意:df命令返回的是1K-blocks,需转换为字节(*1024)
|
||||
var process = new System.Diagnostics.Process
|
||||
{
|
||||
StartInfo = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = "df",
|
||||
Arguments = $"-P {hostMountPoint}", // -P 确保输出格式一致
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
|
||||
process.Start();
|
||||
string output = process.StandardOutput.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
|
||||
if (process.ExitCode != 0)
|
||||
{
|
||||
return (false, 0);
|
||||
}
|
||||
|
||||
// 解析df输出(第二行为数据行)
|
||||
var lines = output.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (lines.Length < 2)
|
||||
{
|
||||
return (false, 0);
|
||||
}
|
||||
|
||||
var dataParts = lines[1].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (dataParts.Length >= 2 && long.TryParse(dataParts[1], out long blocks))
|
||||
{
|
||||
return (true, blocks * 1024); // 1K-blocks -> 字节
|
||||
}
|
||||
|
||||
return (false, 0);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return (false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字节转GB(1GB = 1024^3字节)
|
||||
/// </summary>
|
||||
private static string ConvertBytesToGb(long bytes)
|
||||
{
|
||||
if (bytes <= 0) return "磁盘空间计算错误";
|
||||
double gb = (double)bytes / (1024 * 1024 * 1024);
|
||||
return $"{gb:F2} GB";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace dy.net.utils
|
||||
{
|
||||
public class DySyncBaseParamDics
|
||||
public class DouyinBaseParamDics
|
||||
{
|
||||
public static Dictionary<string, string> CollectParams { get; } = InitializeUserCollecParams();
|
||||
public static Dictionary<string, string> FavoriteParams { get; } = InitializeUserFavoriteParams();
|
||||
+1
-1
@@ -7,7 +7,7 @@ namespace dy.net.utils
|
||||
public static class Md5Util
|
||||
{
|
||||
|
||||
public static string JWT_TOKEN_KEY = "dy.net-key-" + IdGener.GetGuid();
|
||||
public static string JWT_TOKEN_KEY = "dysync.net-key-" + IdGener.GetGuid();
|
||||
public static string Md5(this string inputString)
|
||||
{
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace dy.net.utils
|
||||
public class NfoFileGenerator
|
||||
{
|
||||
|
||||
public static void GenerateNfoFile(VideoNfo videoInfo, string filePath)
|
||||
public static void GenerateNfoFile(DouyinVideoNfo videoInfo, string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -44,6 +44,36 @@ namespace dy.net.utils
|
||||
}
|
||||
}
|
||||
|
||||
// --- 新增:处理演员信息 ---
|
||||
if (videoInfo.Actors != null && videoInfo.Actors.Any())
|
||||
{
|
||||
var actorsElement = new XElement("actors");
|
||||
foreach (var actor in videoInfo.Actors)
|
||||
{
|
||||
// 至少需要演员姓名
|
||||
if (!string.IsNullOrWhiteSpace(actor.Name))
|
||||
{
|
||||
var actorElement = new XElement("actor");
|
||||
actorElement.Add(new XElement("name", CleanInvalidXmlChars(actor.Name)));
|
||||
|
||||
// 可选的角色和头像
|
||||
if (!string.IsNullOrWhiteSpace(actor.Role))
|
||||
actorElement.Add(new XElement("role", CleanInvalidXmlChars(actor.Role)));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(actor.Thumb))
|
||||
actorElement.Add(new XElement("thumb", CleanInvalidXmlChars(actor.Thumb)));
|
||||
|
||||
actorsElement.Add(actorElement);
|
||||
}
|
||||
}
|
||||
// 将整个 <actors> 节点添加到根节点
|
||||
if (actorsElement.HasElements)
|
||||
{
|
||||
root.Add(actorsElement);
|
||||
}
|
||||
}
|
||||
// --- 演员信息处理结束 ---
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.Thumbnail))
|
||||
root.Add(new XElement("thumb", new XAttribute("aspect", "poster"), CleanInvalidXmlChars(videoInfo.Thumbnail)));
|
||||
|
||||
@@ -58,11 +88,18 @@ namespace dy.net.utils
|
||||
root
|
||||
);
|
||||
|
||||
// 确保目录存在
|
||||
string directory = Path.GetDirectoryName(filePath);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
doc.Save(filePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Error($"生成 {videoInfo?.Title ?? "未知视频"}, NFO文件时出错: {ex.Message}");
|
||||
Serilog.Log.Error(ex, $"生成 {videoInfo?.Title ?? "未知视频"} 的 NFO 文件时出错。");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
using dy.net.dto;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace dy.net.utils
|
||||
{
|
||||
/// <summary>
|
||||
/// NFO文件生成器,负责将VideoInfo对象转换为XML格式的NFO文件
|
||||
/// </summary>
|
||||
public class NfoGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// 生成视频NFO文件
|
||||
/// </summary>
|
||||
/// <param name="videoInfo">视频信息对象,包含所有需要写入NFO的元数据</param>
|
||||
/// <param name="outputPath">输出文件的完整路径,包括文件名</param>
|
||||
public void GenerateNfoFile(VideoNFOInfo videoInfo, string outputPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建根元素,电影用"movie",电视剧集用"episodedetails",电视节目用"tvshow"
|
||||
XElement root = new XElement("movie");
|
||||
|
||||
// 添加基本信息
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.Title))
|
||||
root.Add(new XElement("title", videoInfo.Title));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.OriginalTitle))
|
||||
root.Add(new XElement("originaltitle", videoInfo.OriginalTitle));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.SortTitle))
|
||||
root.Add(new XElement("sorttitle", videoInfo.SortTitle));
|
||||
|
||||
if (videoInfo.Year > 0)
|
||||
root.Add(new XElement("year", videoInfo.Year));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.Plot))
|
||||
root.Add(new XElement("plot", videoInfo.Plot));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.Outline))
|
||||
root.Add(new XElement("outline", videoInfo.Outline));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.Tagline))
|
||||
root.Add(new XElement("tagline", videoInfo.Tagline));
|
||||
|
||||
// 添加人员信息
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.Director))
|
||||
root.Add(new XElement("director", videoInfo.Director));
|
||||
|
||||
// 添加演员
|
||||
foreach (var actor in videoInfo.Actors)
|
||||
{
|
||||
root.Add(new XElement("actor",
|
||||
new XElement("name", actor)
|
||||
));
|
||||
}
|
||||
|
||||
// 添加编剧
|
||||
foreach (var writer in videoInfo.Writers)
|
||||
{
|
||||
root.Add(new XElement("writer", writer));
|
||||
}
|
||||
|
||||
// 添加媒体信息
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.Genre))
|
||||
root.Add(new XElement("genre", videoInfo.Genre));
|
||||
|
||||
if (videoInfo.Rating > 0)
|
||||
root.Add(new XElement("rating", videoInfo.Rating));
|
||||
|
||||
if (videoInfo.Votes > 0)
|
||||
root.Add(new XElement("votes", videoInfo.Votes));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.Studio))
|
||||
root.Add(new XElement("studio", videoInfo.Studio));
|
||||
|
||||
if (videoInfo.Premiered.HasValue)
|
||||
root.Add(new XElement("premiered", videoInfo.Premiered.Value.ToString("yyyy-MM-dd")));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.Runtime))
|
||||
root.Add(new XElement("runtime", videoInfo.Runtime));
|
||||
|
||||
// 添加文件信息
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.FileName))
|
||||
root.Add(new XElement("filenameandpath", videoInfo.FileName));
|
||||
|
||||
if (videoInfo.FileSize > 0)
|
||||
root.Add(new XElement("filesize", videoInfo.FileSize));
|
||||
|
||||
// 添加新增字段
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.Country))
|
||||
root.Add(new XElement("country", videoInfo.Country));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.Language))
|
||||
root.Add(new XElement("language", videoInfo.Language));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.VideoCodec))
|
||||
root.Add(new XElement("codec", videoInfo.VideoCodec));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.AudioCodec))
|
||||
root.Add(new XElement("audiocodec", videoInfo.AudioCodec));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(videoInfo.Resolution))
|
||||
root.Add(new XElement("resolution", videoInfo.Resolution));
|
||||
|
||||
// 创建文档并保存
|
||||
XDocument doc = new XDocument(
|
||||
new XDeclaration("1.0", "UTF-8", "yes"),
|
||||
root
|
||||
);
|
||||
|
||||
// 确保目录存在
|
||||
var directory = Path.GetDirectoryName(outputPath);
|
||||
if (!string.IsNullOrWhiteSpace(directory) && !Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
doc.Save(outputPath);
|
||||
Console.WriteLine($"NFO文件已生成: {outputPath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"生成NFO文件时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,6 +22,17 @@ namespace dy.net.utils
|
||||
/// 非法字符替换后的占位符(也可设为空字符串)
|
||||
/// </summary>
|
||||
private const string IllegalCharReplacement = "";
|
||||
|
||||
|
||||
// 修正:使用 \U 前缀来表示超过 \uFFFF 的Unicode码点
|
||||
private static readonly Regex _emojiRegex = new Regex(
|
||||
@"[\u1F600-\u1F64F\u1F300-\u1F5FF\u1F680-\u1F6FF\U0001E000-\U0001EFFF\u2600-\u2B55\u200D]",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex _hashtagRegex = new Regex(@"\#\S+", RegexOptions.Compiled);
|
||||
private static readonly Regex _invalidCharsRegex;
|
||||
private static readonly Regex _multipleUnderscoresRegex = new Regex(@"_+", RegexOptions.Compiled);
|
||||
|
||||
#endregion
|
||||
|
||||
#region 处理抖音视频文件名
|
||||
@@ -42,7 +53,7 @@ namespace dy.net.utils
|
||||
// 3. 长度控制:按UTF-8字节数截断(避免超系统限制)
|
||||
string truncatedTitle = TruncateByByteLength(purifiedTitle, MaxFileNameBytes);
|
||||
|
||||
return truncatedTitle;
|
||||
return truncatedTitle.Trim();
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -57,7 +68,7 @@ namespace dy.net.utils
|
||||
title = id;
|
||||
}
|
||||
// 步骤1:移除话题标签(#xxx 或 #xxx#yyy)
|
||||
title = Regex.Replace(title, @"#\S+", "", RegexOptions.Compiled);
|
||||
//title = Regex.Replace(title, @"#\S+", "", RegexOptions.Compiled);
|
||||
|
||||
// 步骤2:移除表情符号(匹配常见表情Unicode区块)
|
||||
string emojiPattern = @"[\u1F600-\u1F64F\u1F300-\u1F5FF\u1F680-\u1F6FF\u1E000-\u1EFFF\u2600-\u2B55\u200D]";
|
||||
@@ -81,7 +92,7 @@ namespace dy.net.utils
|
||||
title = Regex.Replace(title, $"{Separator}+", Separator.ToString(), RegexOptions.Compiled);
|
||||
|
||||
// 步骤6:移除首尾无效字符(分隔符、点号)
|
||||
title = title.Trim(Separator, '.');
|
||||
title = title.Replace(" ","").Trim(Separator, '.');
|
||||
|
||||
// 容错:如果净化后为空,返回默认值
|
||||
return string.IsNullOrWhiteSpace(title) ? id : title;
|
||||
@@ -123,28 +134,35 @@ namespace dy.net.utils
|
||||
// 清理路径中的特殊字符(避免创建文件夹失败)
|
||||
public static string SanitizePath(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
try
|
||||
{
|
||||
path = "其他";
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
path = "其他";
|
||||
}
|
||||
|
||||
// 步骤1:移除话题标签(#xxx 或 #xxx#yyy)
|
||||
//path = Regex.Replace(path, @"#\S+", "", RegexOptions.Compiled);
|
||||
|
||||
// 步骤2:移除表情符号(匹配常见表情Unicode区块)
|
||||
//string emojiPattern = @"[\u1F600-\u1F64F\u1F300-\u1F5FF\u1F680-\u1F6FF\u1E000-\u1EFFF\u2600-\u2B55\u200D]";
|
||||
//path = Regex.Replace(path, emojiPattern, "", RegexOptions.Compiled);
|
||||
|
||||
|
||||
foreach (var c in Path.GetInvalidFileNameChars())
|
||||
{
|
||||
path = path.Replace(c, '_');
|
||||
}
|
||||
if (path.Length > 50)
|
||||
{
|
||||
path = path.Substring(0, 50);
|
||||
}
|
||||
return path.Trim().Replace(" ", "");
|
||||
}
|
||||
|
||||
// 步骤1:移除话题标签(#xxx 或 #xxx#yyy)
|
||||
path = Regex.Replace(path, @"#\S+", "", RegexOptions.Compiled);
|
||||
|
||||
// 步骤2:移除表情符号(匹配常见表情Unicode区块)
|
||||
string emojiPattern = @"[\u1F600-\u1F64F\u1F300-\u1F5FF\u1F680-\u1F6FF\u1E000-\u1EFFF\u2600-\u2B55\u200D]";
|
||||
path = Regex.Replace(path, emojiPattern, "", RegexOptions.Compiled);
|
||||
|
||||
|
||||
foreach (var c in Path.GetInvalidFileNameChars())
|
||||
catch (Exception ex)
|
||||
{
|
||||
path = path.Replace(c, '_');
|
||||
return path;
|
||||
}
|
||||
if (path.Length > 50)
|
||||
{
|
||||
path = path.Substring(0, 50);
|
||||
}
|
||||
return path.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user