一個大而全的.NET幫助類庫-Masuit.Tools

一個大而全的.NET幫助類庫-Masuit.Tools

包含一些常用的操作類,大都是靜態類

最後更新 2023/3/11 下午2:58
懒得勤快
預計閱讀 38 分鐘
分類
.NET
標籤
.NET C#
  • 倉庫地址:https://github.com/ldqk/Masuit.Tools

LICENSE nuget nuget codeSize language

包含一些常用的操作類,大都是靜態類,加密解密,反射操作,權重隨機篩選演算法,分散式短 id,運算式樹,linq 擴充,檔案壓縮,多執行緒下載和 FTP 用戶端,硬體資訊,字串擴充方法,日期時間擴充操作,中國農曆,大檔案複製,影像裁切,驗證碼,斷點續傳,集合擴充、Excel 匯出等常用封裝。諸多功能集一身,程式碼量不到 2MB!

官網教學

Masuit Tools

專案開發模式:日常程式碼累積+網路蒐集
⭐⭐⭐ 喜歡這個專案的話就 Star、Fork、Follow 素質三連關 ♂ 注一下吧 ⭐⭐⭐
關於本專案,如果你有任何不懂的地方或使用過程中遇到任何問題,可以直接提 issue 或私訊聯絡我,我會為你提供完全免費的技術指導,當然,如果你覺得不好意思接受免費的指導,想適當打賞我也是不會拒絕的!🤣🤣🤣

本專案已得到JetBrains的支持!

Star 趨勢

請注意:

一旦使用本開源專案以及引用了本專案或包含本專案程式碼的公司因為違反勞動法(包括但不限定非法裁員、超時用工、僱用童工等)在任何法律訴訟中敗訴的,一經發現,本專案作者有權利追討本專案的使用費(公司工商登記資訊認繳金額的 2-5 倍作為本專案的授權費),或者直接不允許使用任何包含本專案的原始碼!任何性質的外包公司996公司需要使用本類別庫,請聯絡作者進行商業授權!其他企業或個人可隨意使用不受限。996 那叫用人,也是廢人。8 小時工作制才可以讓你時間自我提升,將來有競爭力。反對 996,人人有責!

建議開發環境

作業系統:Windows 10 1903 及以上版本
開發工具:VisualStudio2019 v16.5 及以上版本
SDK:.NET Core 2.1.0 及以上所有版本

安裝套件

基礎功能套件

.NET Framework版本的套件因打包環境異常,無法正常發佈更新,目前暫時停更~

.NET Framework ≥ 4.6.1

PM> Install-Package Masuit.Tools.Net

.NET Standard ≥ 2.1 或只想使用一些基本功能

通用專案推薦首選套件

PM> Install-Package Masuit.Tools.Abstraction

.NET Core ≥ 2.1

.NET Core專案推薦首選套件

PM> Install-Package Masuit.Tools.Core

.NET Framework 4.5 特供版

請注意:這是.NET Framework 4.5的專用版本,相比4.6.1及.NET Core的版本,閹割了Redis、HTML、檔案壓縮、ASP.NET擴充、硬體監測、Session擴充等一些功能。如果你的專案版本高於 4.5,請務必使用上述版本的套件,以享受完整的功能體驗!

PM> Install-Package Masuit.Tools.Net45

加值套件

Masuit.Tools.AspNetCore

AspNetCore專案推薦首選套件

ASP.NET Core Web 專用套件,包含 Masuit.Tools.Core 的全部功能,並且增加了一些對 ASP.NET Core Web 功能的額外支援。

Masuit.Tools.Excel

Excel 匯入匯出的專用獨立套件

Masuit.Tools.NoSQL.MongoDBClient

mongodb 的封裝操作類獨立套件

為工具庫註冊設定

工具庫需要用到外部設定區段,.NET Framework 專案設定在 web.config/app.config 的 AppSettings 設定區段中,.NET Core 專案設定在 appsettings.json 中:

  1. EmailDomainWhiteList,郵箱驗證需要用到的白名單網域,英文逗號分隔,每個元素支援正則表達式,若未設定,則不啟用郵箱驗證白名單,範例: "^\\w{1,5}@qq.com,^\\w{1,5}@163.com,^\\w{1,5}@gmail.com,^\\w{1,5}@outlook.com"

  2. EmailDomainBlockList,郵箱驗證需要用到的黑名單網域,英文逗號分隔,每個元素支援正則表達式,且黑名單優先級高於白名單,若未設定,則不啟用郵箱驗證黑白名單

public Startup(IConfiguration configuration)
{
    configuration.AddToMasuitTools(); // 若未呼叫,則預設自動嘗試載入appsettings.json
}

特色功能範例程式碼

線上體驗

https://replit.com/@ldqk/MasuitToolsDemo?v=1#main.cs

1.檢驗字串是否是 Email、手機號、URL、IP 位址、身分證字號等

var (isMatch, match) = "337845818@qq.com".MatchEmail(); // 可在appsetting.json中添加EmailDomainWhiteList和EmailDomainBlockList設定郵箱網域黑白名單,逗號分隔,如"EmailDomainBlockList": "^\\w{1,5}@qq.com,^\\w{1,5}@163.com,^\\w{1,5}@gmail.com,^\\w{1,5}@outlook.com",
bool isInetAddress = "114.114.114.114".MatchInetAddress();
bool isUrl = "http://ldqk.org/20/history".MatchUrl();
bool isPhoneNumber = "15205201520".MatchPhoneNumber();
bool isIdentifyCard = "312000199502230660".MatchIdentifyCard();// 校驗中國大陸身分證字號
bool isCNPatentNumber = "200410018477.9".MatchCNPatentNumber(); // 校驗中國專利申請號或專利號,是否帶校驗位,校驗位前是否帶“.”,都可以校驗,待校驗的號碼前不要帶CN、ZL字樣的前綴

2.硬體監測(僅支援 Windows,部分函式僅支援實體機模式)

float load = SystemInfo.CpuLoad;// 取得CPU使用率
long physicalMemory = SystemInfo.PhysicalMemory;// 取得實體記憶體總數
long memoryAvailable = SystemInfo.MemoryAvailable;// 取得實體記憶體可用率
double freePhysicalMemory = SystemInfo.GetFreePhysicalMemory();// 取得可用實體記憶體
double temperature = SystemInfo.GetCPUTemperature();// 取得CPU溫度
int cpuCount = SystemInfo.GetCpuCount();// 取得CPU核心數
var ipAddress = SystemInfo.GetLocalIPs();// 取得本機所有IP位址
string localUsedIp = SystemInfo.GetLocalUsedIP();// 取得本機目前正在使用的IP位址
IList<string> macAddress = SystemInfo.GetMacAddress();// 取得本機所有網卡mac位址
string osVersion = Windows.GetOsVersion();// 取得作業系統版本
RamInfo ramInfo = SystemInfo.GetRamInfo();// 取得記憶體資訊
var cpuSN=SystemInfo.GetCpuInfo()[0].SerialNumber; // CPU序號
var driveSN=SystemInfo.GetDiskInfo()[0].SerialNumber; // 硬碟序號

// 快速方法
var cpuInfos = CpuInfo.Locals; // 快速取得CPU的資訊
var ramInfo = RamInfo.Local; // 快速取得記憶體的資訊
var diskInfos = DiskInfo.Locals; // 快速取得硬碟的資訊
var biosInfo = BiosInfo.Local; // 快速取得主機板的資訊

3.html 的防 XSS 處理:

string html = @"<link href='/Content/font-awesome/css' rel='stylesheet'/>
        <!--[if IE 7]>
        <link href='/Content/font-awesome-ie7.min.css' rel='stylesheet'/>
        <![endif]-->
        <script src='/Scripts/modernizr'></script>
        <div id='searchBox' role='search'>
        <form action='/packages' method='get'>
        <span class='user-actions'><a href='/users/account/LogOff'>退出</a></span>
        <input name='q' id='searchBoxInput'/>
        <input id='searchBoxSubmit' type='submit' value='Submit' />
        </form>
        </div>";
string s = html.HtmlSantinizerStandard();//清理後:<div><span><a href="/users/account/LogOff">退出</a></span></div>

4.整理 Windows 系統的記憶體:

類似於各大系統優化軟體的加速球功能

Windows.ClearMemorySilent();

5.任意進位轉換

可用於生成短 id,短 hash,隨機字串等操作,純數學運算。

NumberFormater nf = new NumberFormater(36);//內建2-62進位的轉換
//NumberFormater nf = new NumberFormater("0123456789abcdefghijklmnopqrstuvwxyz");// 自訂進位字元,可用於生成驗證碼
string s36 = nf.ToString(12345678);
long num = nf.FromString("7clzi");
Console.WriteLine("12345678的36進位是:" + s36); // 7clzi
Console.WriteLine("36進位的7clzi是:" + num); // 12345678
var s = new NumberFormater(62).ToString(new Random().Next(100000, int.MaxValue)); //配合隨機數生成隨機字串
//擴充方法形式呼叫
var bin=12345678.ToBase(36);// 10進位轉36進位:7clzi
var num="7clzi".FromBase(36);// 36進位轉10進位:12345678
//超大數字的進位轉換
var num = "e6186159d38cd50e0463a55e596336bd".FromBaseBig(16); // 大數字16進位轉10進位
Console.WriteLine(num); // 十進位:305849028665645097422198928560410015421
Console.WriteLine(num.ToBase(64)); // 64進位:3C665pQUPl3whzFlVpoPqZ,22位長度
Console.WriteLine(num.ToBase(36)); // 36進位:dmed4dkd5bhcg4qdktklun0zh,25位長度
Console.WriteLine(num.ToBase(7)); // 7進位:2600240311641665565300424545154525131265221035,46位長度
Console.WriteLine(num.ToBase(12)); // 12進位:5217744842749978a756b22135b16a5998a5,36位長度
Console.WriteLine(num.ToBase(41)); // 41進位:opzeBda2aytcEeudEquuesbk,24位長度

6.納秒級效能計時器

HiPerfTimer timer = HiPerfTimer.StartNew();
for (int i = 0; i < 100000; i++)
{
    //todo
}
timer.Stop();
Console.WriteLine("執行for迴圈100000次耗時"+timer.Duration+"s");
double time = HiPerfTimer.Execute(() =>
{
    for (int i = 0; i < 100000; i++)
    {
        //todo
    }
});
Console.WriteLine("執行for迴圈100000次耗時"+time+"s");

7.產生分散式唯一有序短 id(雪花 id)

var sf = SnowFlake.GetInstance();
string token = sf.GetUniqueId();// rcofqodori0w
string shortId = sf.GetUniqueShortId(8);// qodw9728
var set = new HashSet<string>();
double time = HiPerfTimer.Execute(() =>
{
    for (int i = 0; i < 1000000; i++)
    {
        set.Add(SnowFlake.GetInstance().GetUniqueId());
    }
});
Console.WriteLine(set.Count == 1000000); //True
Console.WriteLine("產生100w個id耗時" + time + "s"); //2.6891495s

8.農曆轉換

ChineseCalendar.CustomHolidays.Add(DateTime.Parse("2018-12-31"),"元旦節");//自訂節假日
ChineseCalendar today = new ChineseCalendar(DateTime.Parse("2018-12-31"));
Console.WriteLine(today.ChineseDateString);// 二零一八年十一月廿五
Console.WriteLine(today.AnimalString);// 生肖:狗
Console.WriteLine(today.GanZhiDateString);// 干支:戊戌年甲子月丁酉日
Console.WriteLine(today.DateHoliday);// 取得按公曆計算的節假日
...

9.Linq 運算式樹擴充

Expression<Func<string, bool>> where1 = s => s.StartsWith("a");
Expression<Func<string, bool>> where2 = s => s.Length > 10;
Func<string, bool> func = where1.And(where2)
    .AndIf(!string.IsNullOrEmpty(name),s=>s==name)
    .Compile(); // And和AndIf可供選擇,滿足條件再執行And
bool b=func("abcd12345678");//true
Expression<Func<string, bool>> where1 = s => s.StartsWith("a");
Expression<Func<string, bool>> where2 = s => s.Length > 10;
Func<string, bool> func = where1
    .Or(where2)
    .OrIf(!string.IsNullOrEmpty(name),s=>s==name)
    .Compile(); // Or和OrIf可供選擇,滿足條件再執行Or
bool b=func("abc");// true
queryable.WhereIf(!string.IsNullOrEmpty(name),e=>e.Name==name)
    .WhereIf(()=> age.HasValue,e=>e.Age>=age); // IQueryable的WhereIf擴充函式,滿足條件再執行Where

10.範本引擎

var tmp = new Template("{{name}},你好!");
tmp.Set("name", "萬金油");
string s = tmp.Render();//萬金油,你好!
var tmp = new Template("{{one}},{{two}},{{three}}");
string s = tmp.Set("one", "1").Set("two", "2").Set("three", "3").Render();// 1,2,3
var tmp = new Template("{{name}},{{greet}}!");
tmp.Set("name", "萬金油");
string s = tmp.Render();// throw 範本變數{{greet}}未被使用

11.List 轉 Datatable

var list = new List<MyClass>()
{
    new MyClass()
    {
        Name = "張三",
        Age = 22
    },
    new MyClass()
    {
        Name = "李四",
        Age = 21
    },
    new MyClass()
    {
        Name = "王五",
        Age = 28
    }
};
var table = list.Select(c => new{姓名=c.Name,年齡=c.Age}).ToDataTable();// 將自動填入列姓名和年齡

12.檔案壓縮解壓

.NET Framework

MemoryStream ms = SevenZipCompressor.ZipStream(new List<string>()
{
    @"D:\1.txt",
    "http://ww3.sinaimg.cn/large/87c01ec7gy1fsq6rywto2j20je0d3td0.jpg",
});//壓縮成記憶體串流
SevenZipCompressor.Zip(new List<string>()
{
    @"D:\1.txt",
    "http://ww3.sinaimg.cn/large/87c01ec7gy1fsq6rywto2j20je0d3td0.jpg",
}, zip);//壓縮成zip
SevenZipCompressor.UnRar(@"D:\Download\test.rar", @"D:\Download\");//解壓rar
SevenZipCompressor.Decompress(@"D:\Download\test.tar", @"D:\Download\");//自動識別解壓壓縮包
SevenZipCompressor.Decompress(@"D:\Download\test.7z", @"D:\Download\");

ASP.NET Core

Startup.cs

services.AddSevenZipCompressor();

建構函式注入 ISevenZipCompressor

private readonly ISevenZipCompressor _sevenZipCompressor;
public Test(ISevenZipCompressor sevenZipCompressor)
{
    _sevenZipCompressor = sevenZipCompressor;
}

使用方式同.NET Framework 版本

13.簡易日誌元件(又不是不能用.jpg)

LogManager.LogDirectory=AppDomain.CurrentDomain.BaseDirectory+"/logs";
LogManager.Event+=info =>
{
    //todo:註冊一些事件操作
};
LogManager.Info("記錄一次訊息");
LogManager.Error(new Exception("例外訊息"));

14.FTP 用戶端

FtpClient ftpClient = FtpClient.GetAnonymousClient("192.168.2.2");//建立一個匿名訪問的用戶端
//FtpClient ftpClient = FtpClient.GetClient("192.168.2.3","admin","123456");// 建立一個帶用戶名密碼的用戶端
ftpClient.Delete("/1.txt");// 刪除檔案
ftpClient.Download("/test/2.txt","D:\\test\\2.txt");// 下載檔案
ftpClient.UploadFile("/test/22.txt","D:\\test\\22.txt",(sum, progress) =>
{
    Console.WriteLine("已上傳:"+progress*1.0/sum);
});//上傳檔案並檢測進度
List<string> files = ftpClient.GetFiles("/");//列出ftp服務端檔案清單
...

15.多執行緒背景下載

var mtd = new MultiThreadDownloader("https://attachments-cdn.shimo.im/yXwC4kphjVQu06rH/KeyShot_Pro_7.3.37.7z",Environment.GetEnvironmentVariable("temp"),"E:\\Downloads\\KeyShot_Pro_7.3.37.7z",8);
mtd.Configure(req =>
 {
     req.Referer = "https://masuit.com";
     req.Headers.Add("Origin", "https://baidu.com");
});
mtd.TotalProgressChanged+=(sender, e) =>
{
    var downloader = sender as MultiThreadDownloader;
    Console.WriteLine("下載進度:"+downloader.TotalProgress+"%");
    Console.WriteLine("下載速度:"+downloader.TotalSpeedInBytes/1024/1024+"MBps");
};
mtd.FileMergeProgressChanged+=(sender, e) =>
{
    Console.WriteLine("下載完成");
};
mtd.FileMergedComplete+=(sender,e)=>{
    Console.WriteLine("檔案合併完成");
};
mtd.Start();//開始下載
//mtd.Pause(); // 暫停下載
//mtd.Resume(); // 繼續下載

16.加密解密/hash

var enc="123456".MDString();// MD5
var enc="123456".MDString("abc");// MD5加鹽
var enc="123456".MDString2();// MD5兩次
var enc="123456".MDString2("abc");// MD5兩次加鹽
var enc="123456".MDString3();// MD5三次
var enc="123456".MDString3("abc");// MD5三次加鹽

string aes = "123456".AESEncrypt();// AES加密為密文
string s = aes.AESDecrypt(); //AES解密為明文
string aes = "123456".AESEncrypt("abc");// AES金鑰加密為密文
string s = aes.AESDecrypt("abc"); //AES金鑰解密為明文

string enc = "123456".DesEncrypt();// DES加密為密文
string s = enc.DesDecrypt(); //DES解密為明文
string enc = "123456".DesEncrypt("abcdefgh");// DES金鑰加密為密文
string s = enc.DesDecrypt("abcdefgh"); //DES金鑰解密為明文

RsaKey rsaKey = RsaCrypt.GenerateRsaKeys();// 產生RSA金鑰對
string encrypt = "123456".RSAEncrypt(rsaKey.PublicKey);// 公開金鑰加密
string s = encrypt.RSADecrypt(rsaKey.PrivateKey);// 私鑰解密

string s = "123".Crc32();// 生成crc32摘要
string s = "123".Crc64();// 生成crc64摘要
string s = "123".SHA256();// 生成SHA256摘要

string pub="hello,world!";
string hidden="ldqk";
var str = pub.InjectZeroWidthString(hidden); // 擴充函式呼叫:將"ldqk"以零寬字串的方式隱藏在"hello,world!"中
var str = ZeroWidthCodec.Encrypt(pub,hidden); // 類別呼叫:將"ldqk"以零寬字串的方式隱藏在"hello,world!"中
var dec = str.DecodeZeroWidthString(); // 擴充函式呼叫:將包含零寬字串的密文解密出隱藏字串"ldqk"
var dec = ZeroWidthCodec.Decrypt(str); // 類別呼叫:將包含零寬字串的密文解密出隱藏字串"ldqk"
var enc = hidden.EncodeToZeroWidthText(); // 擴充函式呼叫:將字串編碼成零寬字串
var enc = ZeroWidthCodec.Encode(); // 類別呼叫:將字串編碼成零寬字串

17.實體驗證

public class MyClass
{
    [IsEmail] //可在appsetting.json中添加EmailDomainWhiteList設定郵箱網域白名單,逗號分隔
    public string Email { get; set; }

    [IsPhone]
    public string PhoneNumber { get; set; }

    [IsIPAddress]
    public string IP { get; set; }

    [MinValue(0, ErrorMessage = "年齡最小為0歲"), MaxValue(100, ErrorMessage = "年齡最大100歲")]
    public int Age { get; set; }

    [ComplexPassword]//密碼複雜度驗證
    public string Password { get; set; }

    [EnumOf] // 檢測是否是有效列舉值
    public MyEnum MyEnum { get; set; }

    [MinItemsCount(1)] // 檢測集合元素最少1個
    public List<string> Strs { get; set; }
}

18.HTML 操作

List<string> srcs = "html".MatchImgSrcs().ToList();// 取得html字串裡所有的img標籤的src屬性
var imgTags = "html".MatchImgTags();//取得html字串裡的所有img標籤
var str="html".RemoveHtmlTag(); // 去除html標籤
...

19.DateTime 擴充

double milliseconds = DateTime.Now.GetTotalMilliseconds();// 取得毫秒級時間戳
double microseconds = DateTime.Now.GetTotalMicroseconds();// 取得微秒級時間戳
double nanoseconds = DateTime.Now.GetTotalNanoseconds();// 取得納秒級時間戳
double seconds = DateTime.Now.GetTotalSeconds();// 取得秒級時間戳
double minutes = DateTime.Now.GetTotalMinutes();// 取得分鐘級時間戳
...

20.IP 位址和 URL

bool inRange = "192.168.2.2".IpAddressInRange("192.168.1.1","192.168.3.255");// 判斷IP位址是否在這個位址段裡
bool isPrivateIp = "172.16.23.25".IsPrivateIP();// 判斷是否是私有位址
bool isExternalAddress = "http://baidu.com".IsExternalAddress();// 判斷是否是外網的URL

//以下需要設定baiduAK
string isp = "114.114.114.114".GetISP(); // 取得ISP運營商資訊
PhysicsAddress physicsAddress = "114.114.114.114".GetPhysicsAddressInfo().Result;// 取得詳細地理資訊物件
Tuple<string, List<string>> ipAddressInfo = "114.114.114.114".GetIPAddressInfo().Result;// 取得詳細地理資訊集合

21.元素去重

var list = new List<MyClass>()
{
    new MyClass()
    {
        Email = "1@1.cn"
    },
    new MyClass()
    {
        Email = "1@1.cn"
    },
    new MyClass()
    {
        Email = "1@1.cn"
    }
};
List<MyClass> classes = list.DistinctBy(c => c.Email).ToList();
Console.WriteLine(classes.Count==1);//True

22.列舉擴充

public enum MyEnum
{
    [Display(Name = "讀")]
    [Description("讀")]
    Read,

    [Display(Name = "寫")]
    [Description("寫")]
    Write
}
Dictionary<int, string> dic1 = typeof(MyEnum).GetDictionary();// 取得列舉值和字串表示的字典映射
var dic2 = typeof(MyEnum).GetDescriptionAndValue();// 取得字串表示和列舉值的字典映射
string desc = MyEnum.Read.GetDescription();// 取得Description標籤
string display = MyEnum.Read.GetDisplay();// 取得Display標籤的Name屬性
var value = typeof(MyEnum).GetValue("Read");//取得字串表示值對應的列舉值
string enumString = 0.ToEnumString(typeof(MyEnum));// 取得列舉值對應的字串表示

23.定長佇列和 ConcurrentHashSet 實作

如果是.NET5及以上,推薦使用框架自帶的Channel實作該功能

LimitedQueue<string> queue = new LimitedQueue<string>(32);// 宣告一個容量為32個元素的定長佇列
ConcurrentLimitedQueue<string> queue = new ConcurrentLimitedQueue<string>(32);// 宣告一個容量為32個元素的執行緒安全定長佇列
var set = new ConcurrentHashSet<string>(); // 用法和hashset保持一致

24.反射操作

MyClass myClass = new MyClass();
PropertyInfo[] properties = myClass.GetProperties();// 取得屬性清單
myClass.SetProperty("Email","1@1.cn");//給物件設定值
myClass.DeepClone(); // 物件深拷貝,帶巢狀層級的

25.取得執行緒內唯一物件

CallContext<T>.SetData("db",dbContext);//設定執行緒內唯一物件
CallContext<T>.GetData("db");//取得執行緒內唯一物件

26.郵件發送

new Email()
{
    SmtpServer = "smtp.masuit.com",// SMTP伺服器
    SmtpPort = 25, // SMTP伺服器連接埠
    EnableSsl = true,//使用SSL
    Username = "admin@masuit.com",// 郵箱用戶名
    Password = "123456",// 郵箱密碼
    Tos = "10000@qq.com,10001@qq.com", //收件人
    Subject = "測試郵件",//郵件標題
    Body = "你好啊",//郵件內容
}.SendAsync(s =>
{
    Console.WriteLine(s);// 發送成功後的回呼
});// 非同步發送郵件

27.影像的簡單處理

ImageUtilities.CompressImage(@"F:\src\1.jpg", @"F:\dest\2.jpg");//無失真壓縮圖片

"base64".SaveDataUriAsImageFile();// 將Base64編碼轉換成圖片

Image image = Image.FromFile(@"D:\1.jpg");
image.MakeThumbnail(@"D:\2.jpg", 120, 80, ThumbnailCutMode.LockWidth);//生成縮圖

Bitmap bmp = new Bitmap(@"D:\1.jpg");
Bitmap newBmp = bmp.BWPic(bmp.Width, bmp.Height);//轉換成黑白
Bitmap newBmp = bmp.CutAndResize(new Rectangle(0, 0, 1600, 900), 160, 90);//裁剪並縮放
bmp.RevPicLR(bmp.Width, bmp.Height);//左右鏡像
bmp.RevPicUD(bmp.Width, bmp.Height);//上下鏡像

var marker=ImageWatermarker(stream);
stream=maker.AddWatermark("浮水印文字","字型檔案",字型大小,color,浮水印位置,邊距); // 給圖片添加浮水印
stream=maker.AddWatermark(浮水印圖片,浮水印位置,邊距,字型大小,字型); // 給圖片添加浮水印

// 影像相似度比對
var hasher = new ImageHasher();
var hash1 = hasher.DifferenceHash256("圖片1"); // 使用差分雜湊演算法計算影像的256位元雜湊
var hash2 = hasher.DifferenceHash256("圖片2"); // 使用差分雜湊演算法計算影像的256位元雜湊
//var hash1 = hasher.AverageHash64("圖片1"); // 使用平均值演算法計算影像的64位元雜湊
//var hash2 = hasher.AverageHash64("圖片2"); // 使用平均值演算法計算影像的64位元雜湊
//var hash1 = hasher.DctHash("圖片1"); // 使用DCT演算法計算影像的64位元雜湊
//var hash2 = hasher.DctHash("圖片2"); // 使用DCT演算法計算影像的64位元雜湊
//var hash1 = hasher.MedianHash64("圖片1"); // 使用中位數演算法計算給定影像的64位元雜湊
//var hash2 = hasher.MedianHash64("圖片2"); // 使用中位數演算法計算給定影像的64位元雜湊
var sim=ImageHasher.Compare(hash1,hash2); // 圖片的相似度,範圍:[0,1]

var imageFormat=stream.GetImageType(); // 取得圖片的真實格式

28.隨機數

Random rnd = new Random();
int num = rnd.StrictNext();//產生真隨機數
double gauss = rnd.NextGauss(20,5);//產生常態高斯分佈的隨機數
var s = new NumberFormater(62).ToString(new Random().Next(100000, int.MaxValue));//生成隨機字串

29.權重篩選功能

var data=new List<WeightedItem<string>>()
{
     new WeightedItem<string>("A", 1),
     new WeightedItem<string>("B", 3),
     new WeightedItem<string>("C", 4),
     new WeightedItem<string>("D", 4),
};
var item=data.WeightedItem();//按權重選出1個元素
var list=data.WeightedItems(2);//按權重選出2個元素
var selector = new WeightedSelector<string>(new List<WeightedItem<string>>()
{
    new WeightedItem<string>("A", 1),
    new WeightedItem<string>("B", 3),
    new WeightedItem<string>("C", 4),
    new WeightedItem<string>("D", 4),
});
var item = selector.Select();//按權重選出1個元素
var list = selector.SelectMultiple(3);//按權重選出3個元素

30.EF Core 支援 AddOrUpdate 方法

/// <summary>
/// 按Id新增或更新文章實體
/// </summary>
public override Post SavePost(Post t)
{
    DataContext.Set<Post>().AddOrUpdate(t => t.Id, t);
    return t;
}

31.敏感資訊遮罩

"13123456789".Mask(); // 131****5678
"admin@masuit.com".MaskEmail(); // a****n@masuit.com

32.集合擴充

var list = new List<string>()
{
    "1","3","3","3"
};
list.AddRangeIf(s => s.Length > 1, "1", "11"); // 將被添加元素中長度大於1的元素添加到list
list.AddRangeIfNotContains("1", "11"); // 將被添加元素中不包含的元素添加到list
list.RemoveWhere(s => s.Length<1); // 將集合中長度小於1的元素移除
list.InsertAfter(0, "2"); // 在第一個元素之後插入
list.InsertAfter(s => s == "1", "2"); // 在元素"1"後插入
var dic = list.ToDictionarySafety(s => s); // 安全的轉換成字典類型,當鍵重複時只添加一個鍵
var dic = list.ToConcurrentDictionary(s => s); // 轉換成並發字典類型,當鍵重複時只添加一個鍵
var dic = list.ToDictionarySafety(s => s, s => s.GetHashCode()); // 安全的轉換成字典類型,當鍵重複時只添加一個鍵
dic.AddOrUpdate("4", 4); // 新增或更新鍵值對
dic.AddOrUpdate(new Dictionary<string, int>()
{
    ["5"] = 5,["55"]=555
}); // 批次新增或更新鍵值對
dic.AddOrUpdate("5", 6, (s, i) => 66); // 如果是新增,則值為6,若更新則值為66
dic.AddOrUpdate("5", 6, 666); // 如果是新增,則值為6,若更新則值為666
dic.GetOrAdd("7",77); // 字典取得或新增元素
dic.GetOrAdd("7",()=>77); // 字典取得或新增元素
dic.AsConcurrentDictionary(); // 普通字典轉換成並發字典集合
var table=list.ToDataTable(); // 轉換成DataTable類型
table.AddIdentityColumn(); //給DataTable增加一個自增列
table.HasRows(); // 檢查DataTable 是否有資料行
table.ToList<T>(); // datatable轉List
var set = list.ToHashSet(s=>s.Name);// 轉HashSet
var cts = new CancellationTokenSource(100); //取消口令
await list.ForeachAsync(async i=>{
    await Task.Delay(100);
    Console.WriteLine(i);
},cts.Token); // 非同步foreach

await list.ForAsync(async (item,index)=>{
    await Task.Delay(100);
    Console.WriteLine(item+"_"+index);
},cts.Token); // 非同步for,帶索引編號
await list.SelectAsync(async i=>{
    await Task.Delay(100);
    return i*10;
}); // 非同步Select
await list.SelectAsync(async (item,index)=>{
    await Task.Delay(100);
    return item*10;
}); // 非同步Select,帶索引編號
string s=list.Join(",");//將字串集合連接成逗號分隔的單字串
var max=list.MaxOrDefault(); // 取最大值,當集合為空的時候不會報錯
var max=list.MaxOrDefault(selector); // 取最大值,當集合為空的時候不會報錯
var max=list.MaxOrDefault(selector,default); // 取最大值,當集合為空的時候不會報錯
var max=list.MinOrDefault(); // 取最小值,當集合為空的時候不會報錯
var max=list.MinOrDefault(selector); // 取最小值,當集合為空的時候不會報錯
var max=list.MinOrDefault(selector,default); // 取最小值,當集合為空的時候不會報錯
var stdDev=list.Select(s=>s.ConvertTo<int>()).StandardDeviation(); // 求標準差

var pages=queryable.ToPagedList(1,10); // 分頁查詢
var pages=await queryable.ToPagedListAsync(1,10); // 分頁查詢

var nums=Enumerable.Range(1, 10).ExceptBy(Enumerable.Range(5, 10), i => i); // 按欄位取差集
var nums=Enumerable.Range(1, 10).IntersectBy(Enumerable.Range(5, 10), i => i); // 按欄位取交集
var nums=Enumerable.Range(1, 10).SequenceEqual(Enumerable.Range(5, 10), i => i); // 判斷序列相等
var nums=Enumerable.Range(1, 10).OrderByRandom(); // 隨機排序

// 多個集合取交集
var list=new List<List<MyClass>>(){
    new List<MyClass>(){
        new MyClass(){Name="aa",Age=11},
        new MyClass(){Name="bb",Age=12},
        new MyClass(){Name="cc",Age=13},
    },
    new List<MyClass>(){
        new MyClass(){Name="bb",Age=12},
        new MyClass(){Name="cc",Age=13},
        new MyClass(){Name="dd",Age=14},
    },
    new List<MyClass>(){
        new MyClass(){Name="cc",Age=13},
        new MyClass(){Name="dd",Age=14},
        new MyClass(){Name="ee",Age=15},
    },
};
var sect=list.IntersectAll(m=>m.Name); // new MyClass(){Name="cc",Age=13}

var list=new List<List<int>>(){
    new(){1,2,3},
    new(){2,3,4},
    new(){3,4,5}
};
var sect=list.IntersectAll();// [3]

// 集合元素改變其索引位置
list.ChangeIndex(item,3); // 將元素item的索引位置變為第3個
list.ChangeIndex(t=>t.Id=="123",2); // 將id為123的元素的索引位置變為第2個

33.Mime 類型

var mimeMapper = new MimeMapper();
var ext = mimeMapper.GetExtensionFromMime("image/jpeg"); // .jpg
var mime = mimeMapper.GetMimeFromExtension(".jpg"); // image/jpeg

34.日期時間擴充

DateTime.Now.GetTotalSeconds(); // 取得該時間相對於1970-01-01 00:00:00的秒數
DateTime.Now.GetTotalMilliseconds(); // 取得該時間相對於1970-01-01 00:00:00的毫秒數
DateTime.Now.GetTotalMicroseconds(); // 取得該時間相對於1970-01-01 00:00:00的微秒數
DateTime.Now.GetTotalNanoseconds(); // 取得該時間相對於1970-01-01 00:00:00的納秒數
var indate=DateTime.Parse("2020-8-3").In(DateTime.Parse("2020-8-2"),DateTime.Parse("2020-8-4"));//true
DateTime time="2021-1-1 8:00:00".ToDateTime(); //字串轉DateTime

//時間段計算工具
var range = new DateTimeRange(DateTime.Parse("2020-8-3"), DateTime.Parse("2020-8-5"));
range.Union(DateTime.Parse("2020-8-4"), DateTime.Parse("2020-8-6")); //連接兩個時間段,結果:2020-8-3~2020-8-6
range.In(DateTime.Parse("2020-8-3"), DateTime.Parse("2020-8-6"));//判斷是否在某個時間段內,true
var (intersected,range2) = range.Intersect(DateTime.Parse("2020-8-4"), DateTime.Parse("2020-8-6"));//兩個時間段是否相交,(true,2020-8-3~2020-8-4)
range.Contains(DateTime.Parse("2020-8-3"), DateTime.Parse("2020-8-4"));//判斷是否包含某個時間段,true
...

35.串流相關

stream.SaveAsMemoryStream(); // 任意串流轉換成記憶體串流
stream.ToArray(); // 任意串流轉換成二進位陣列
stream.ToArrayAsync(); // 任意串流轉換成二進位陣列
stream.ShuffleCode(); // 串流洗碼,在串流的末端隨即增加幾個空位元組,重要資料請謹慎使用,可能造成串流損壞

// 集區化記憶體串流,用法與MemorySteam保持一致
using var ms=PooledMemoryStream();

// 大型記憶體串流,最大可支援1TB記憶體資料,推薦當資料串流大於2GB時使用,用法與MemorySteam保持一致
using var ms=LargeMemoryStream();

//檔案串流快速複製
FileStream fs = new FileStream(@"D:\boot.vmdk", FileMode.OpenOrCreate, FileAccess.ReadWrite);
{
        //fs.CopyToFile(@"D:\1.bak");//同步複製大檔案
        fs.CopyToFileAsync(@"D:\1.bak");//非同步複製大檔案
        string md5 = fs.GetFileMD5Async().Result;//非同步取得檔案的MD5
        string sha1 = fs.GetFileSha1();//非同步取得檔案的SHA1
}
memoryStream.SaveFile("filename"); // 將記憶體串流轉儲成檔案

36.數值轉換

1.2345678901.Digits8(); // 將小數截斷為8位
1.23.ConvertTo<int>(); // 小數轉int
1.23.ConvertTo<T>(); // 小數轉T基本類型
bool b=1.23.TryConvertTo<T>(out result); // 小數轉T基本類型
var num=1.2345.ToDecimal(2); //轉decimal並保留兩位小數

37.INI 設定檔操作(僅支援 Windows)

INIFile ini=new INIFile("filename.ini");
ini.IniWriteValue(section,key,value); // 寫值
ini.IniReadValue(section,key); // 讀值
ini.ClearAllSection(); // 清空所有設定區段
ini.ClearSection(section); // 清空設定區段

38.雷達圖計算引擎

應用場景:計算兩個多邊形的相似度,用戶畫像之類的

var points=RadarChartEngine.ComputeIntersection(chart1,chart2); //取得兩個多邊形的相交區域
points.ComputeArea(); //計算多邊形面積

39.樹狀結構實作

基本介面類別:
ITreeChildren:帶 Children 屬性的介面
ITreeParent:帶 Parent 屬性的介面
ITree:繼承 ITreeParent 和 ITreeChildren,同時多了 Name 屬性

相關擴充方法:

trees.Filter(func); // 從樹狀集合中過濾
trees.Flatten(); // 將資料平鋪開
tree.AllChildren(); // 取得所有的子級
tree.AllParent(); // 取得所有的父級
tree.IsRoot(); // 是否是根節點
tree.IsLeaf(); // 是否是葉子節點
tree.Level(); // 所處深度/層級
tree.Path(); // 完整路徑

var tree=list.ToTree(c => c.Id, c => c.Pid);//繼承自ITreeParent<T>, ITreeChildren<T>的集合轉換成樹狀結構
var tree=list.ToTreeGeneral(c => c.Id, c => c.Pid);//一般的集合轉換成樹狀結構

40.簡單的 Excel 匯出

需要額外依賴套件:Masuit.Tools.Excel

var stream=list.Select(item=>new{
    姓名=item.Name,
    年齡=item.Age,
    item.Gender,
    Avatar=Image.FromStream(filestream) //圖片列
}).ToDataTable().ToExcel("Sheet1"); //自訂列名匯出
var stream=list.ToDataTable("Sheet1").ToExcel("檔案密碼");

一些約定規則:

  1. 圖片列支援 Stream、Bitmap、IEnumerable、IEnumerable、IDictionary<string,Stream>、IDictionary<string,MemoryStream>、IDictionary<string,Bitmap>類型;
  2. 其中,如果是 IDictionary 類型的圖片列,字典的鍵為圖片超連結的完整 url;
  3. 預設欄位名稱作為列名匯出;
  4. 若 list 是一個具體的強類型,預設會先查找每個欄位的 Description 標記,若有 Description 標記,則取 Description 標記作為列名顯示
  5. ToExcel 方法支援 DataTable、List、Dictionary<string, DataTable>類型的直接呼叫

41.EFCore 實體比對功能

取得指定實體的變更

var changes=dbContext.GetChanges<Post>();//取得變更欄位資訊
var added=dbContext.GetAdded<Post>();//取得新增的實體欄位資訊
var removed=dbContext.GetRemoved<Post>();//取得被移除的實體欄位資訊
var allchanges=dbContext.GetAllChanges<Post>();//取得增刪改的實體欄位資訊

取得所有實體的變更

var changes=dbContext.GetChanges();//取得變更欄位資訊
var added=dbContext.GetAdded();//取得新增的實體欄位資訊
var removed=dbContext.GetRemoved();//取得被移除的實體欄位資訊
var allchanges=dbContext.GetAllChanges();//取得增刪改的實體欄位資訊

比對資訊包含屬性資訊、舊值、新值、實體資訊、鍵資訊、變更狀態等

42.任何類型支援鏈式呼叫

a.Next(func1).Next(func2).Next(func3);
"123".Next(s=>s.ToInt32()).Next(x=>x*2).Next(x=>Math.Log(x));

43.Newtonsoft.Json 的只允許欄位反序列化行為的契約解釋器

DeserializeOnlyContractResolver

該解釋器針對類別屬性被 DeserializeOnlyJsonPropertyAttribute 標記的,在反序列化的時候生效,在序列化的時候忽略

public class ClassDto
    {
        [DeserializeOnlyJsonProperty]
        public string MyProperty { get; set; }

        public int Num { get; set; }
    }

    JsonConvert.SerializeObject(new MyClass(),new JsonSerializerSettings()
    {
        ContractResolver = new DeserializeOnlyContractResolver() // 設定使用DeserializeOnlyContractResolver解釋器
    });

如果是 WebAPI 全域使用:

        //在Startup.ConfigureServices中
        services.AddMvc().AddNewtonsoftJson(options =>
             {
                 var resolver = new DeserializeOnlyContractResolver();
                 resolver.NamingStrategy = new CamelCaseNamingStrategy();
                 options.SerializerSettings.ContractResolver = resolver;
             });

FallbackJsonPropertyResolver

該解釋器針對某個屬性設定多個別名,反序列化時支援多個別名 key 進行綁定,彌補官方 JsonProperty 別名屬性只能設定單一別名的不足

    public class ClassDto
    {
        [FallbackJsonProperty("MyProperty","a","b")]
        public string MyProperty { get; set; }

        public int Num { get; set; }
    }

    JsonConvert.SerializeObject(new MyClass(),new JsonSerializerSettings()
    {
        ContractResolver = new FallbackJsonPropertyResolver() // 設定使用FallbackJsonPropertyResolver解釋器
    });

CompositeContractResolver

該解釋器是 DeserializeOnlyContractResolver 和 FallbackJsonPropertyResolver 的融合版

44. ASP.NET Core Action 同時支援 queryString、表單和 json 請求類型的模型繫結器 BodyOrDefaultModelBinder

用法:

引入套件:Masuit.Tools.AspNetCore

PM> Install-Package Masuit.Tools.AspNetCore

Startup 設定:

    services.AddMvc(options =>
        {
             options.ModelBinderProviders.InsertBodyOrDefaultBinding();
        })

在 action 的參數模型前打上標記:[FromBodyOrDefault]即可,當然也可以省略,範例程式碼如下:

        [HttpGet("query"),HttpPost("query")]
        public IActionResult Query([FromBodyOrDefault]QueryModel query)
        {
            return Ok(...);
        }

        [HttpGet("query"),HttpPost("query")]
        public IActionResult Query([FromBodyOrDefault]int id,[FromBodyOrDefault]string name)
        {
            return Ok(...);
        }

45. 字串 SimHash 相似度演算法

var dis="12345678".HammingDistance("1234567");
var dis=new SimHash("12345678").HammingDistance(new SimHash("1234567"));

46. 真實檔案類型探測

// 多種方式,任君呼叫
var detector=new FileInfo(filepath).DetectFiletype();
//var detector=File.OpenRead(filepath).DetectFiletype();
//var detector=FileSignatureDetector.DetectFiletype(filepath);
detector.Precondition;//基礎檔案類型
detector.Extension;//真實副檔名
detector.MimeType;//MimeType
detector.FormatCategories;//格式類別

預設支援的檔案類型

副檔名 說明
3GP 3GPP, 3GPP 2
7Z 7-Zip
APK ZIP based Android Package
AVI Audio-Video Interleave
SH Shell Script
BPLIST Binary Property List
BMP, DIB Bitmap
BZ2 Bunzip2 Compressed
CAB Microsoft Cabinet
CLASS Java Bytecode
CONFIG .NET Configuration File
CRT, CERT Certificate
CUR Cursor
DB Windows Thumbs.db Thumbnail Database
DDS DirectDraw Surface
DLL Windows Dynamic Linkage Library
DMG Apple Disk Mount Image
DMP Windows Memory Dump File
DOC Microsoft Office Word 97-2003 Document
DOCX Microsoft Office Word OpenXML Document
EPUB e-Pub Document
EXE Windows Executive
FLAC Loseless Audio
FLV Flash Video
GIF Graphics Interchage Format
GZ GZ Compressed
HDP HD Photo(JPEG XR) Image
HWP Legacy HWP, HWPML, CFBF HWP
ICO Icon
INI Initialization File
ISO ISO-9660 Disc Image
LNK Windows Shortcut Link
JP2 JPEG 2000 Image
JPG, JPEG Joint Photographic Experts Group Image
LZH LZH Compressed
M4A MP4 Container Contained Audio Only
M4V MP4 Container Contained Video
MID Midi Sound
MKA Matroska Container Contained Audio Only
MKV Matroska Container Contained Video
MOV QuickTime Movie Video
MP4 MP4 Container Contained Contents
MSI Microsoft Installer
OGG OGG Video or Audio
ODF OpenDocument Formula
ODG OpenDocument Graphics
ODP OpenDocument Presentation
ODS OpenDocument Spreadsheet
ODT OpenDocument Text
PAK PAK Archive or Quake Archive
PDB Microsoft Program Database
PDF Portable Document Format
PFX Microsoft Personal Information Exchange Certificate
PNG Portable Network Graphics Image
PPT Microsoft Office PowerPoint 97-2003 Document
PPTX Microsoft Office PowerPoint OpenXML Document
PPSX Microsoft Office PowerPoint OpenXML Document for Slideshow only
PSD Photoshop Document
RAR WinRAR Compressed
REG Windows Registry
RPM RedHat Package Manager Package
RTF Rich Text Format Document
SLN Microsoft Visual Studio Solution
SRT SubRip Subtitle
SWF Shockwave Flash
SQLITE, DB SQLite Database
TAR pre-ISO Type and UStar Type TAR Package
TIFF Tagged Image File Format Image
TXT Plain Text
WAV Wave Audio
WASM Binary WebAssembly
WEBM WebM Video
WEBP WebP Image
XAR XAR Package
XLS Microsoft Office Excel 97-2003 Document
XLSX Microsoft Office Excep OpenXML Document
XML Extensible Markup Language Document
Z Z Compressed
ZIP ZIP Package

Asp.Net MVC 和 ASP.NET Core 的支援斷點續傳和多執行緒下載的 ResumeFileResult

在 ASP.NET Core 中透過 MVC/WebAPI 應用程式傳輸檔案資料時使用斷點續傳以及多執行緒下載支援。

它提供了ETag標頭以及Last-Modified標頭。 它還支援以下前置條件標頭:If-MatchIf-None-MatchIf-Modified-SinceIf-Unmodified-SinceIf-Range

支援 ASP.NET Core 2.0+

從.NET Core2.0 開始,ASP.NET Core 內部支援斷點續傳。 因此只是對 FileResult 做了一些擴充。 只留下了“Content-Disposition” Inline 的一部分。 所有程式碼都依賴於基礎.NET 類。

如何使用

.NET Framework

在你的控制器中,你可以像在FileResult一樣的方式使用它。

using Masuit.Tools.Mvc;
using Masuit.Tools.Mvc.ResumeFileResult;
private readonly MimeMapper mimeMapper=new MimeMapper(); // 推薦使用相依性注入

public ActionResult ResumeFileResult()
{
    var path = Server.MapPath("~/Content/test.mp4");
    return new ResumeFileResult(path, mimeMapper.GetMimeFromPath(path), Request);
}

public ActionResult ResumeFile()
{
    return this.ResumeFile("~/Content/test.mp4", mimeMapper.GetMimeFromPath(path), "test.mp4");
}

public ActionResult ResumePhysicalFile()
{
    return this.ResumePhysicalFile(@"D:/test.mp4", mimeMapper.GetMimeFromPath(@"D:/test.mp4"), "test.mp4");
}

ASP.NET Core

要使用 ResumeFileResults,必須在Startup.csConfigureServices方法呼叫中設定服務:

using Masuit.Tools.AspNetCore.ResumeFileResults.Extensions;
public void ConfigureServices(IServiceCollection services)
{
    services.AddResumeFileResult();
}

然後在你的控制器中,你可以像在FileResult一樣的方式使用它。

點擊查看程式碼
using Masuit.Tools.AspNetCore.ResumeFileResults.Extensions;
private const string EntityTag = "\"TestFile\"";

private readonly IHostingEnvironment _hostingEnvironment;

private readonly DateTimeOffset _lastModified = new DateTimeOffset(2016, 1, 1, 0, 0, 0, TimeSpan.Zero);

/// <summary>
///
/// </summary>
/// <param name="hostingEnvironment"></param>
public TestController(IHostingEnvironment hostingEnvironment)
{
    _hostingEnvironment = hostingEnvironment;
}

[HttpGet("content/{fileName}/{etag}")]
public IActionResult FileContent(bool fileName, bool etag)
{
    string webRoot = _hostingEnvironment.WebRootPath;
    var content = System.IO.File.ReadAllBytes(Path.Combine(webRoot, "TestFile.txt"));
    ResumeFileContentResult result = this.ResumeFile(content, "text/plain", fileName ? "TestFile.txt" : null, etag ? EntityTag : null);
    result.LastModified = _lastModified;
    return result;
}

[HttpGet("content/{fileName}")]
public IActionResult FileContent(bool fileName)
{
    string webRoot = _hostingEnvironment.WebRootPath;
    var content = System.IO.File.ReadAllBytes(Path.Combine(webRoot, "TestFile.txt"));
    var result = new ResumeFileContentResult(content, "text/plain")
    {
        FileInlineName = "TestFile.txt",
        LastModified = _lastModified
    };
    return result;
}

[HttpHead("file")]
public IActionResult FileHead()
{
    ResumeVirtualFileResult result = this.ResumeFile("TestFile.txt", "text/plain", "TestFile.txt", EntityTag);
    result.LastModified = _lastModified;
    return result;
}

[HttpPut("file")]
public IActionResult FilePut()
{
    ResumeVirtualFileResult result = this.ResumeFile("TestFile.txt", "text/plain", "TestFile.txt", EntityTag);
    result.LastModified = _lastModified;
    return result;
}

[HttpGet("stream/{fileName}/{etag}")]
public IActionResult FileStream(bool fileName, bool etag)
{
    string webRoot = _hostingEnvironment.WebRootPath;
    FileStream stream = System.IO.File.OpenRead(Path.Combine(webRoot, "TestFile.txt"));

    ResumeFileStreamResult result = this.ResumeFile(stream, "text/plain", fileName ? "TestFile.txt" : null, etag ? EntityTag : null);
    result.LastModified = _lastModified;
    return result;
}

[HttpGet("stream/{fileName}")]
public IActionResult FileStream(bool fileName)
{
    string webRoot = _hostingEnvironment.WebRootPath;
    FileStream stream = System.IO.File.OpenRead(Path.Combine(webRoot, "TestFile.txt"));

    var result = new ResumeFileStreamResult(stream, "text/plain")
    {
        FileInlineName = "TestFile.txt",
        LastModified = _lastModified
    };

    return result;
}

[HttpGet("physical/{fileName}/{etag}")]
public IActionResult PhysicalFile(bool fileName, bool etag)
{
    string webRoot = _hostingEnvironment.WebRootPath;

    ResumePhysicalFileResult result = this.ResumePhysicalFile(Path.Combine(webRoot, "TestFile.txt"), "text/plain", fileName ? "TestFile.txt" : null, etag ? EntityTag : null);
    result.LastModified = _lastModified;
    return result;
}

[HttpGet("physical/{fileName}")]
public IActionResult PhysicalFile(bool fileName)
{
    string webRoot = _hostingEnvironment.WebRootPath;

    var result = new ResumePhysicalFileResult(Path.Combine(webRoot, "TestFile.txt"), "text/plain")
    {
        FileInlineName = "TestFile.txt",
        LastModified = _lastModified
    };

    return result;
}

[HttpGet("virtual/{fileName}/{etag}")]
public IActionResult VirtualFile(bool fileName, bool etag)
{
    ResumeVirtualFileResult result = this.ResumeFile("TestFile.txt", "text/plain", fileName ? "TestFile.txt" : null, etag ? EntityTag : null);
    result.LastModified = _lastModified;
    return result;
}

以上範例將為您的資料提供“Content-Disposition:attachment”。 當沒有提供 fileName 時,資料將作為“Content-Disposition:inline”提供。 另外,它可以提供ETagLastModified標頭。

[HttpGet("virtual/{fileName}")]
public IActionResult VirtualFile(bool fileName)
{
    var result = new ResumeVirtualFileResult("TestFile.txt", "text/plain")
    {
        FileInlineName = "TestFile.txt",
        LastModified = _lastModified
    };
    return result;
}

推薦專案

基於 EntityFrameworkCore 和 Lucene.NET 實現的全文檢索搜尋引擎:Masuit.LuceneEFCore.SearchEngine

開源部落格系統:Masuit.MyBlogs

繼續探索

延伸閱讀

更多文章
同分類 / 同標籤 2026/2/7

AOT使用經驗總結

從專案建立伊始,就應養成良好的習慣,即只要添加了新功能或使用了較新的語法,就及時進行 AOT 發布測試。

繼續閱讀