├── src └── dotnet-cnblogs │ ├── program.ico │ ├── Properties │ └── launchSettings.json │ ├── Command │ ├── ICommand.cs │ ├── CommandContext.cs │ ├── CommandContextStore.cs │ ├── CommandReset.cs │ ├── CommandUploadImg.cs │ ├── CommandSetConfig.cs │ └── CommandProcessFile.cs │ ├── TagHandlers │ ├── ITagHandler.cs │ └── ImageHandler.cs │ ├── Utils │ ├── ConsoleHelper.cs │ ├── ImageUploadHelper.cs │ ├── FileEncodingType.cs │ ├── TeaHelper.cs │ └── MimeMapping.cs │ ├── publish.sh │ ├── dotnet-cnblog.csproj │ └── Program.cs ├── assets ├── 668104-20201127164440482-852371747.png ├── 668104-20201127164512348-139991479.png ├── 668104-20201127164728833-2082113229.png ├── 668104-20201127164901378-1830341075.png ├── 668104-20201127164440482-852371747-1606467064830.png ├── 668104-20201127164512348-139991479-1606467064830.png └── 668104-20201127164728833-2082113229-1606467064830.png ├── dotnet-cnblogs-tool.sln.DotSettings ├── LICENSE ├── dotnet-cnblogs-tool.sln ├── README.md ├── .gitignore └── .github └── workflows └── release_stable_cli.yaml /src/dotnet-cnblogs/program.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stulzq/dotnet-cnblogs-tool/HEAD/src/dotnet-cnblogs/program.ico -------------------------------------------------------------------------------- /assets/668104-20201127164440482-852371747.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stulzq/dotnet-cnblogs-tool/HEAD/assets/668104-20201127164440482-852371747.png -------------------------------------------------------------------------------- /assets/668104-20201127164512348-139991479.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stulzq/dotnet-cnblogs-tool/HEAD/assets/668104-20201127164512348-139991479.png -------------------------------------------------------------------------------- /assets/668104-20201127164728833-2082113229.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stulzq/dotnet-cnblogs-tool/HEAD/assets/668104-20201127164728833-2082113229.png -------------------------------------------------------------------------------- /assets/668104-20201127164901378-1830341075.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stulzq/dotnet-cnblogs-tool/HEAD/assets/668104-20201127164901378-1830341075.png -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "dotnet-cnblog": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /assets/668104-20201127164440482-852371747-1606467064830.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stulzq/dotnet-cnblogs-tool/HEAD/assets/668104-20201127164440482-852371747-1606467064830.png -------------------------------------------------------------------------------- /assets/668104-20201127164512348-139991479-1606467064830.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stulzq/dotnet-cnblogs-tool/HEAD/assets/668104-20201127164512348-139991479-1606467064830.png -------------------------------------------------------------------------------- /assets/668104-20201127164728833-2082113229-1606467064830.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stulzq/dotnet-cnblogs-tool/HEAD/assets/668104-20201127164728833-2082113229-1606467064830.png -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Command/ICommand.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Dotnetcnblog.Command 4 | { 5 | public interface ICommand 6 | { 7 | void Execute(CommandContext context); 8 | } 9 | } -------------------------------------------------------------------------------- /src/dotnet-cnblogs/TagHandlers/ITagHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Dotnetcnblog.TagHandlers 4 | { 5 | public interface ITagHandler 6 | { 7 | List Process(string content); 8 | } 9 | } -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Command/CommandContext.cs: -------------------------------------------------------------------------------- 1 | using MetaWeblogClient; 2 | 3 | namespace Dotnetcnblog.Command 4 | { 5 | public class CommandContext 6 | { 7 | public string AppConfigFilePath { get; set; } 8 | public byte[] EncryptKey => new byte[] { 21, 52, 33, 78, 52, 45 }; 9 | public BlogConnectionInfo ConnectionInfo { get; set; } 10 | 11 | } 12 | } -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Command/CommandContextStore.cs: -------------------------------------------------------------------------------- 1 | namespace Dotnetcnblog.Command 2 | { 3 | public class CommandContextStore 4 | { 5 | private static CommandContext _context; 6 | public static void Set(CommandContext context) 7 | { 8 | _context = context; 9 | } 10 | 11 | public static CommandContext Get() 12 | { 13 | return _context; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /dotnet-cnblogs-tool.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | True 3 | True 4 | True -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Utils/ConsoleHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using Colorful; 3 | 4 | namespace Dotnetcnblog.Utils 5 | { 6 | public class ConsoleHelper 7 | { 8 | static readonly Color MsgColor = Color.FromArgb(0, 204, 51); 9 | static readonly Color ErrorColor = Color.FromArgb(153, 0, 51); 10 | 11 | public static void PrintMsg(string text) 12 | { 13 | Console.WriteLine(text, MsgColor); 14 | } 15 | 16 | public static void PrintError(string text) 17 | { 18 | Console.WriteLine(text, ErrorColor); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Command/CommandReset.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Dotnetcnblog.Utils; 3 | using McMaster.Extensions.CommandLineUtils; 4 | 5 | namespace Dotnetcnblog.Command 6 | { 7 | [Command(Name = "reset", Description = "重置配置")] 8 | public class CommandReset : ICommand 9 | { 10 | public int OnExecute(CommandLineApplication app) 11 | { 12 | Execute(CommandContextStore.Get()); 13 | return 0; 14 | } 15 | 16 | public void Execute(CommandContext context) 17 | { 18 | File.Delete(context.AppConfigFilePath); 19 | ConsoleHelper.PrintMsg("配置重置成功!"); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/dotnet-cnblogs/TagHandlers/ImageHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace Dotnetcnblog.TagHandlers 5 | { 6 | /// 7 | /// 图片标签处理程序 8 | /// 9 | public class ImageHandler : ITagHandler 10 | { 11 | private const string MatchRule = @"!\[.*?\]\((.*?)\)"; 12 | 13 | public List Process(string content) 14 | { 15 | var result = new List(); 16 | 17 | var matchResult = Regex.Matches(content, MatchRule, RegexOptions.IgnoreCase | RegexOptions.RightToLeft); 18 | 19 | foreach (Match match in matchResult) result.Add(match.Groups[1].Value); 20 | 21 | return result; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/dotnet-cnblogs/publish.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | dotnet publish -c Release -r win-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true -p:IsTrimmable=true -p:TrimMode=link --self-contained true 4 | dotnet publish -c Release -r win-x86 -p:PublishSingleFile=true -p:PublishTrimmed=true -p:IsTrimmable=true -p:TrimMode=link --self-contained true 5 | dotnet publish -c Release -r osx-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true -p:IsTrimmable=true -p:TrimMode=link --self-contained true 6 | dotnet publish -c Release -r linux-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true -p:IsTrimmable=true -p:TrimMode=link --self-contained true 7 | dotnet publish -c Release -r linux-arm -p:PublishSingleFile=true -p:PublishTrimmed=true -p:IsTrimmable=true -p:TrimMode=link --self-contained true 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Utils/ImageUploadHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using MetaWeblogClient; 4 | using Polly; 5 | 6 | namespace Dotnetcnblog.Utils 7 | { 8 | public class ImageUploadHelper 9 | { 10 | public static Client BlogClient; 11 | 12 | public static void Init(BlogConnectionInfo info) 13 | { 14 | BlogClient = new Client(info); 15 | } 16 | 17 | public static string Upload(string filePath) 18 | { 19 | var policy = Policy.Handle().Retry(3, 20 | (exception, retryCount) => 21 | { 22 | Console.WriteLine("上传失败,正在重试 {0},异常:{1}", retryCount, exception.Message); 23 | }); 24 | try 25 | { 26 | var url = policy.Execute(() => 27 | { 28 | var fileinfo = new FileInfo(filePath); 29 | var mediaObjectInfo = BlogClient.NewMediaObject(fileinfo.Name, MimeMapping.GetMimeMapping(filePath), 30 | File.ReadAllBytes(filePath)); 31 | return mediaObjectInfo.URL; 32 | }); 33 | 34 | return url; 35 | } 36 | catch (Exception e) 37 | { 38 | Console.WriteLine("上传失败,异常:{0}", e.Message); 39 | throw; 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /dotnet-cnblogs-tool.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30711.63 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2A364680-E06B-424C-A860-B191DB766B04}" 7 | ProjectSection(SolutionItems) = preProject 8 | README.md = README.md 9 | EndProjectSection 10 | EndProject 11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-cnblog", "src\dotnet-cnblogs\dotnet-cnblog.csproj", "{C75DAF23-E4DD-404A-B1CD-978DAD3987A3}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {C75DAF23-E4DD-404A-B1CD-978DAD3987A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {C75DAF23-E4DD-404A-B1CD-978DAD3987A3}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {C75DAF23-E4DD-404A-B1CD-978DAD3987A3}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {C75DAF23-E4DD-404A-B1CD-978DAD3987A3}.Release|Any CPU.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {C3EBCEBB-EE56-465C-B548-132C31709785} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Command/CommandUploadImg.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.IO; 5 | using System.Linq; 6 | using Dotnetcnblog.TagHandlers; 7 | using Dotnetcnblog.Utils; 8 | using McMaster.Extensions.CommandLineUtils; 9 | using Console = Colorful.Console; 10 | 11 | namespace Dotnetcnblog.Command 12 | { 13 | [Command(Name = "upload", Description = "上传单个图片")] 14 | public class CommandUploadImg : ICommand 15 | { 16 | private static readonly Dictionary ReplaceDic = new Dictionary(); 17 | 18 | 19 | [Option("-f|--file", Description = "需要上传的图片路径")] 20 | [Required] 21 | public string FilePath { get; set; } 22 | 23 | public int OnExecute(CommandLineApplication app) 24 | { 25 | if (app.Options.Count == 1 && app.Options[0].ShortName == "h") 26 | { 27 | app.ShowHelp(); 28 | } 29 | 30 | Execute(CommandContextStore.Get()); 31 | return 0; 32 | } 33 | 34 | public void Execute(CommandContext context) 35 | { 36 | try 37 | { 38 | if (!File.Exists(FilePath)) 39 | { 40 | ConsoleHelper.PrintError($"文件不存在:{FilePath}"); 41 | } 42 | else 43 | { 44 | 45 | var imgUrl = ImageUploadHelper.Upload(FilePath); 46 | ConsoleHelper.PrintMsg($"{FilePath} 上传成功. {imgUrl}"); 47 | 48 | } 49 | } 50 | catch (Exception e) 51 | { 52 | ConsoleHelper.PrintError(e.Message); 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Command/CommandSetConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.IO; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Dotnetcnblog.Utils; 7 | using McMaster.Extensions.CommandLineUtils; 8 | using MetaWeblogClient; 9 | using Newtonsoft.Json; 10 | using Console = Colorful.Console; 11 | 12 | namespace Dotnetcnblog.Command 13 | { 14 | [Command(Name = "set", Description = "设置配置")] 15 | public class CommandSetConfig : ICommand 16 | { 17 | 18 | public int OnExecute(CommandLineApplication app) 19 | { 20 | Execute(CommandContextStore.Get()); 21 | return 0; 22 | } 23 | 24 | public void Execute(CommandContext context) 25 | { 26 | ConsoleHelper.PrintMsg("请输入博客ID:(如:https://www.cnblogs.com/stulzq 的博客id为 stulzq )"); 27 | var blogId = Console.ReadLine(); 28 | 29 | ConsoleHelper.PrintMsg("请输入用户名:"); 30 | var userName = Console.ReadLine(); 31 | 32 | ConsoleHelper.PrintMsg("请输入密 码:"); 33 | var pwd = Console.ReadLine(); 34 | 35 | var config = new BlogConnectionInfo( 36 | "https://www.cnblogs.com/" + blogId, 37 | "https://rpc.cnblogs.com/metaweblog/" + blogId, 38 | blogId, 39 | userName, 40 | Convert.ToBase64String(TeaHelper.Encrypt(Encoding.UTF8.GetBytes(pwd), context.EncryptKey))); 41 | 42 | var path = Path.GetDirectoryName(context.AppConfigFilePath); 43 | if (!Directory.Exists(path)) 44 | { 45 | Directory.CreateDirectory(path!); 46 | } 47 | 48 | File.WriteAllText(context.AppConfigFilePath, JsonConvert.SerializeObject(config)); 49 | 50 | ConsoleHelper.PrintMsg("配置设置成功!"); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dotNet 博客园工具 2 | 3 | [![](https://img.shields.io/nuget/v/dotnet-cnblog.svg?style=flat-square&label=nuget)](https://www.nuget.org/packages/dotnet-cnblog) 4 | 5 | ## 一.前言 6 | 7 | 此工具解决的痛点是在本地编辑的 Markdown 文件里包含的图片,在博客园发布时,需要手动一张张的复制到博客园的编辑器中上传,十分麻烦,此文中有详细说明:[如何高效的编写与同步博客 (.NET Core 小工具实现)](https://www.cnblogs.com/stulzq/p/9043632.html) 8 | 9 | ## 二.安装工具 10 | 11 | (1)具有 .NET Core/.NET 5 环境可以直接使用命令安装: 12 | 13 | ````shell 14 | dotnet tool install --global dotnet-cnblog 15 | ```` 16 | 17 | (2)如果没有上面的环境,那么可以直接下载二进制文件 18 | 19 | 下载地址: https://github.com/stulzq/dotnet-cnblogs-tool/releases 20 | 21 | > 因为本工具是开源的,而且使用过程中需要输入 Token,所以不要相信任何第三方下载,因为它们有可能被植入恶意代码,仅提供上面两种方式。 22 | 23 | ## 三.使用 24 | 25 | 第一次运行需要配置博客ID,账号、Token等,按照提示输入即可,对密码采用tea加密算法进行加密存储。 26 | 27 | ![](assets/668104-20201127164440482-852371747.png) 28 | 29 | >需要账号、Token 是因为调用 MetaWeblog API 需要此信息 30 | 31 | Token 申请:https://i.cnblogs.com/settings 32 | 33 | ![image](https://user-images.githubusercontent.com/13200155/176429548-bf374aa6-b16f-4b12-a464-c5adcaa86d14.png) 34 | 35 | 2022.6.29 更新,请使用 MetaWeblog Token 替换原来的账户密码!!! 36 | 37 | ### 重置配置 38 | 39 | 使用下面的命令重置配置: 40 | 41 | ````shell 42 | dotnet-cnblog reset 43 | ```` 44 | ![](assets/668104-20201127164512348-139991479.png) 45 | 46 | ### 四.上传图片 47 | 48 | 对Markdown文件里的图片进行解析,上传到博客园,并且转换内容保存到新的文件中。 49 | 50 | ````shell 51 | dotnet-cnblog proc -f 52 | ```` 53 | ![](assets/668104-20201127164728833-2082113229.png) 54 | 55 | 处理过的内容保存在 `Markdown 原始文件名-cnblog.md` 中,复制粘贴到博客园的编辑器发布即可。 56 | 57 | ## 五.其他说明 58 | 59 | - 程序未加过多的容错机制,请勿暴力测试。比如发送一个非MarkDown文件到程序。 60 | 61 | - 上传图片具有重试机制,重试三次。 62 | 63 | - 只有本地路径的图片才会上传,所有http/https远程图片都会过滤 64 | 65 | - 图片上传完毕以后,会自动转换md内容保存到带`cnblog`后缀的文件里面 66 | 67 | - 密码错误请重置配置 68 | 69 | 若上传接口报错,请到博客园后台设置 https://i.cnblogs.com/settings 70 | 71 | ![](assets/668104-20201127164901378-1830341075.png) 72 | 73 | 74 | Windows 设置右键菜单的方法 https://www.cnblogs.com/shengliC/p/14410298.html 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ---> C Sharp 2 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 3 | [Bb]in/ 4 | [Oo]bj/ 5 | 6 | # mstest test results 7 | TestResults 8 | 9 | ## Ignore Visual Studio temporary files, build results, and 10 | ## files generated by popular Visual Studio add-ons. 11 | 12 | # User-specific files 13 | *.suo 14 | *.user 15 | *.sln.docstates 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Rr]elease/ 20 | x64/ 21 | *_i.c 22 | *_p.c 23 | *.ilk 24 | *.meta 25 | *.obj 26 | *.pch 27 | *.pdb 28 | *.pgc 29 | *.pgd 30 | *.rsp 31 | *.sbr 32 | *.tlb 33 | *.tli 34 | *.tlh 35 | *.tmp 36 | *.log 37 | *.vspscc 38 | *.vssscc 39 | .builds 40 | 41 | # Visual C++ cache files 42 | ipch/ 43 | *.aps 44 | *.ncb 45 | *.opensdf 46 | *.sdf 47 | 48 | # Visual Studio profiler 49 | *.psess 50 | *.vsp 51 | *.vspx 52 | 53 | # Guidance Automation Toolkit 54 | *.gpState 55 | 56 | # ReSharper is a .NET coding add-in 57 | _ReSharper* 58 | 59 | # NCrunch 60 | *.ncrunch* 61 | .*crunch*.local.xml 62 | 63 | # Installshield output folder 64 | [Ee]xpress 65 | 66 | # DocProject is a documentation generator add-in 67 | DocProject/buildhelp/ 68 | DocProject/Help/*.HxT 69 | DocProject/Help/*.HxC 70 | DocProject/Help/*.hhc 71 | DocProject/Help/*.hhk 72 | DocProject/Help/*.hhp 73 | DocProject/Help/Html2 74 | DocProject/Help/html 75 | 76 | # Click-Once directory 77 | publish 78 | 79 | # Publish Web Output 80 | *.Publish.xml 81 | 82 | # NuGet Packages Directory 83 | packages 84 | 85 | # Windows Azure Build Output 86 | csx 87 | *.build.csdef 88 | 89 | # Windows Store app package directory 90 | AppPackages/ 91 | 92 | # Others 93 | [Bb]in 94 | [Oo]bj 95 | sql 96 | TestResults 97 | [Tt]est[Rr]esult* 98 | *.Cache 99 | ClientBin 100 | [Ss]tyle[Cc]op.* 101 | ~$* 102 | *.dbmdl 103 | Generated_Code #added for RIA/Silverlight projects 104 | 105 | # Backup & report files from converting an old project file to a newer 106 | # Visual Studio version. Backup files are not needed, because we have git ;-) 107 | _UpgradeReport_Files/ 108 | Backup*/ 109 | UpgradeLog*.XML 110 | 111 | .vs 112 | .idea/ 113 | -------------------------------------------------------------------------------- /src/dotnet-cnblogs/dotnet-cnblog.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | true 6 | true 7 | 8 | true 9 | net5.0 10 | program.ico 11 | dotnet-cnblog 12 | stulzq 13 | stulzq 14 | dotnet-cnblog 15 | Cnblogs Article Quick Publish Tool. 16 | Copyright 2018-2020 stulzq 17 | https://github.com/stulzq/dotnet-cnblogs-tool 18 | 19 | cnblogs 20 | true 21 | 1.4.2 22 | true 23 | true 24 | snupkg 25 | Dotnetcnblog 26 | 兼容路径 27 | LICENSE 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | false 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | True 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Utils/FileEncodingType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace Dotnetcnblog.Utils 6 | { 7 | /// 8 | /// 获取文件的编码格式 9 | /// 10 | public class FileEncodingType 11 | { 12 | /// 13 | /// 给定文件的路径,读取文件的二进制数据,判断文件的编码类型 14 | /// 15 | /// 16 | /// 17 | public static Encoding GetType(string fileName) 18 | { 19 | var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); 20 | var r = GetType(fs); 21 | fs.Close(); 22 | return r; 23 | } 24 | 25 | /// 26 | /// 通过给定的文件流,判断文件的编码类型 27 | /// 28 | /// 29 | /// 30 | public static Encoding GetType(FileStream fs) 31 | { 32 | byte[] unicode = {0xFF, 0xFE, 0x41}; 33 | byte[] unicodeBig = {0xFE, 0xFF, 0x00}; 34 | byte[] utf8 = {0xEF, 0xBB, 0xBF}; //带BOM 35 | var reVal = Encoding.Default; 36 | 37 | var r = new BinaryReader(fs, Encoding.Default); 38 | int.TryParse(fs.Length.ToString(), out var i); 39 | var ss = r.ReadBytes(i); 40 | if (IsUtf8Bytes(ss) || ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF) 41 | reVal = Encoding.UTF8; 42 | else if (ss[0] == 0xFE && ss[1] == 0xFF && ss[2] == 0x00) 43 | reVal = Encoding.BigEndianUnicode; 44 | else if (ss[0] == 0xFF && ss[1] == 0xFE && ss[2] == 0x41) reVal = Encoding.Unicode; 45 | r.Close(); 46 | return reVal; 47 | } 48 | 49 | /// 50 | /// 判断是否是不带 BOM 的 UTF8 格式 51 | /// 52 | /// 53 | /// 54 | private static bool IsUtf8Bytes(byte[] data) 55 | { 56 | var charByteCounter = 1; //计算当前正分析的字符应还有的字节数 57 | foreach (var t in data) 58 | { 59 | var curByte = t; //当前分析的字节. 60 | if (charByteCounter == 1) 61 | { 62 | if (curByte >= 0x80) 63 | { 64 | //判断当前 65 | while (((curByte <<= 1) & 0x80) != 0) charByteCounter++; 66 | //标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X 67 | if (charByteCounter == 1 || charByteCounter > 6) return false; 68 | } 69 | } 70 | else 71 | { 72 | //若是UTF-8 此时第一位必须为1 73 | if ((curByte & 0xC0) != 0x80) return false; 74 | charByteCounter--; 75 | } 76 | } 77 | 78 | if (charByteCounter > 1) throw new Exception("非预期的byte格式"); 79 | return true; 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.IO; 4 | using System.Text; 5 | using Dotnetcnblog.Command; 6 | using Dotnetcnblog.Utils; 7 | using McMaster.Extensions.CommandLineUtils; 8 | using MetaWeblogClient; 9 | using Newtonsoft.Json; 10 | using Console = Colorful.Console; 11 | 12 | namespace Dotnetcnblog 13 | { 14 | [Command(Name = "dotnet-cnblog", Description = "dotNet 博客园工具")] 15 | [Subcommand(typeof(CommandReset))] 16 | [Subcommand(typeof(CommandSetConfig))] 17 | [Subcommand(typeof(CommandProcessFile))] 18 | [Subcommand(typeof(CommandUploadImg))] 19 | class Program 20 | { 21 | private const string CfgFileName = "dotnet-cnblog.config.json"; 22 | 23 | private static int Main(string[] args) 24 | { 25 | PrintTitle(); 26 | if (Init()) 27 | { 28 | return CommandLineApplication.Execute(args); 29 | } 30 | else 31 | { 32 | ConsoleHelper.PrintError("您还未设置配置,将引导你设置!"); 33 | var setConfig=new CommandSetConfig(); 34 | setConfig.Execute(CommandContextStore.Get()); 35 | return 0; 36 | } 37 | } 38 | 39 | /// 40 | /// 一级命令执行 41 | /// 42 | /// 43 | /// 44 | private int OnExecute(CommandLineApplication app) 45 | { 46 | app.ShowHelp(); 47 | return 0; 48 | } 49 | 50 | /// 51 | /// 打印标题 52 | /// 53 | static void PrintTitle() 54 | { 55 | Console.WriteAscii("dotNet Cnblogs Tool", Color.FromArgb(244, 212, 255)); 56 | Console.Write("作者:", Color.FromArgb(90, 212, 255)); 57 | Console.WriteLine("晓晨Master", Color.FromArgb(200, 212, 255)); 58 | Console.Write("问题反馈:", Color.FromArgb(90, 212, 255)); 59 | Console.WriteLine("https://github.com/stulzq/dotnet-cnblogs-tool/issues ", Color.FromArgb(200, 212, 255)); 60 | Console.WriteLine(""); 61 | } 62 | 63 | /// 64 | /// 初始化,加载配置 65 | /// 66 | /// 67 | static bool Init() 68 | { 69 | var context=new CommandContext(); 70 | 71 | var docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); 72 | context.AppConfigFilePath = Path.Combine(docPath,"dotnet-cnblog", CfgFileName); 73 | 74 | if (!File.Exists(context.AppConfigFilePath)) 75 | { 76 | CommandContextStore.Set(context); 77 | return false; 78 | } 79 | 80 | var config = JsonConvert.DeserializeObject(File.ReadAllText(context.AppConfigFilePath)); 81 | config.Password = 82 | Encoding.UTF8.GetString(TeaHelper.Decrypt(Convert.FromBase64String(config.Password), context.EncryptKey)); 83 | context.ConnectionInfo = config; 84 | ImageUploadHelper.Init(config); 85 | CommandContextStore.Set(context); 86 | return true; 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Command/CommandProcessFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Web; 7 | using Dotnetcnblog.TagHandlers; 8 | using Dotnetcnblog.Utils; 9 | using McMaster.Extensions.CommandLineUtils; 10 | using Console = Colorful.Console; 11 | 12 | namespace Dotnetcnblog.Command 13 | { 14 | [Command(Name = "proc", Description = "处理文件")] 15 | public class CommandProcessFile : ICommand 16 | { 17 | private static readonly Dictionary ReplaceDic = new Dictionary(); 18 | 19 | 20 | [Option("-f|--file", Description = "需要处理的文件路径")] 21 | [Required] 22 | public string FilePath { get; set; } 23 | 24 | public int OnExecute(CommandLineApplication app) 25 | { 26 | if (app.Options.Count == 1 && app.Options[0].ShortName == "h") 27 | { 28 | app.ShowHelp(); 29 | } 30 | 31 | Execute(CommandContextStore.Get()); 32 | return 0; 33 | } 34 | 35 | public void Execute(CommandContext context) 36 | { 37 | try 38 | { 39 | if (!File.Exists(FilePath)) 40 | { 41 | ConsoleHelper.PrintError($"文件不存在:{FilePath}"); 42 | } 43 | else 44 | { 45 | var fileDir = new FileInfo(FilePath).DirectoryName; 46 | var fileContent = File.ReadAllText(FilePath); 47 | var imgHandler = new ImageHandler(); 48 | var imgList = imgHandler.Process(fileContent); 49 | 50 | ConsoleHelper.PrintMsg($"提取图片成功,共 {imgList.Count} 个。"); 51 | 52 | //循环上传图片 53 | foreach (var img in imgList) 54 | { 55 | if (img.StartsWith("http", StringComparison.OrdinalIgnoreCase)) 56 | { 57 | ConsoleHelper.PrintMsg($"图片跳过:{img} "); 58 | continue; 59 | } 60 | 61 | try 62 | { 63 | var imgPhyPath = HttpUtility.UrlDecode(Path.Combine(fileDir!, img)); 64 | if (File.Exists(imgPhyPath)) 65 | { 66 | var imgUrl = ImageUploadHelper.Upload(imgPhyPath); 67 | if (!ReplaceDic.ContainsKey(img)) ReplaceDic.Add(img, imgUrl); 68 | ConsoleHelper.PrintMsg($"{img} 上传成功. {imgUrl}"); 69 | } 70 | else 71 | { 72 | ConsoleHelper.PrintMsg($"{img} 未发现文件."); 73 | } 74 | } 75 | catch (Exception e) 76 | { 77 | Console.WriteLine(e.Message); 78 | } 79 | } 80 | 81 | //替换 82 | fileContent = ReplaceDic.Keys.Aggregate(fileContent, (current, key) => current.Replace(key, ReplaceDic[key])); 83 | 84 | var newFileName = FilePath.Substring(0, FilePath.LastIndexOf('.')) + "-cnblog" + Path.GetExtension(FilePath); 85 | File.WriteAllText(newFileName, fileContent, FileEncodingType.GetType(FilePath)); 86 | 87 | ConsoleHelper.PrintMsg($"处理完成!文件保存在:{newFileName}"); 88 | } 89 | } 90 | catch (Exception e) 91 | { 92 | ConsoleHelper.PrintError(e.Message); 93 | } 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /.github/workflows/release_stable_cli.yaml: -------------------------------------------------------------------------------- 1 | name: Release_CLI_Stable 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*.*.*" 7 | - "!*.*.*-beta*" 8 | - "!*.*.*-rc*" 9 | 10 | jobs: 11 | publish_cli: 12 | name: Build and upload cli artifact 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | targets: 17 | [ 18 | "linux-x64", 19 | "osx-x64", 20 | "win-x86", 21 | ] 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v1 25 | - name: Set up .NET Core 26 | uses: actions/setup-dotnet@v1 27 | with: 28 | dotnet-version: "5.0.x" 29 | - name: Publish .NET app 30 | env: 31 | RID: ${{ matrix.targets }} 32 | VERSION: ${{ github.ref_name }} 33 | run: dotnet publish src/dotnet-cnblogs/dotnet-cnblog.csproj -c Release -r $RID -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false -p:EnableCompressionInSingleFile=true --self-contained true --output /home/runner/work/clis/$RID 34 | 35 | - name: Package assets 36 | env: 37 | RID: ${{ matrix.targets }} 38 | VERSION: ${{ github.ref_name }} 39 | run: | 40 | mkdir /home/runner/work/release 41 | ls /home/runner/work/clis/ 42 | zip -j /home/runner/work/release/dotnet-cnblogs.$VERSION.$RID.zip /home/runner/work/clis/$RID/* 43 | - name: Upload artifacts 44 | uses: actions/upload-artifact@v2 45 | with: 46 | name: dotnet-cnblogs 47 | path: /home/runner/work/release 48 | 49 | publish_windows_x64_and_arm_aot: 50 | name: Build and upload windows x64 and arm64 aot cli artifact 51 | runs-on: windows-latest 52 | strategy: 53 | matrix: 54 | targets: 55 | [ 56 | "win-x64", 57 | "win-arm64" 58 | ] 59 | steps: 60 | - name: Checkout 61 | uses: actions/checkout@v1 62 | - name: Setup .NET Core SDK 6.x 63 | uses: actions/setup-dotnet@v2 64 | with: 65 | dotnet-version: 6.x 66 | - name: Publish .NET app 67 | env: 68 | RID: ${{ matrix.targets }} 69 | VERSION: ${{ github.ref_name }} 70 | run: dotnet publish src/dotnet-cnblogs/dotnet-cnblog.csproj -c Release -r $($env:RID) -p:UseAot=true -p:DebugType=None -p:DebugSymbols=false --self-contained true --output ../publish/$($env:RID) 71 | 72 | - name: Package assets 73 | env: 74 | RID: ${{ matrix.targets }} 75 | VERSION: ${{ github.ref_name }} 76 | run: | 77 | mkdir ../release 78 | rm -fo ../publish/$($env:RID)/*.pdb 79 | ls ../publish/ 80 | Compress-Archive -Path ../publish/$($env:RID)/* -DestinationPath ../release/dotnet-cnblogs.$($env:VERSION).$($env:RID).zip 81 | - name: Upload artifacts 82 | uses: actions/upload-artifact@v2 83 | with: 84 | name: dotnet-cnblogs 85 | path: D:/a/dotnet-cnblogs/release 86 | 87 | release_cli: 88 | name: Publish release 89 | needs: ['publish_cli','publish_windows_x64_and_arm_aot'] 90 | runs-on: ubuntu-latest 91 | 92 | steps: 93 | - name: Download build artifacts 94 | uses: actions/download-artifact@v1 95 | with: 96 | name: dotnet-cnblogs 97 | - name: list dotnet-cnblogs 98 | run: ls dotnet-cnblogs 99 | - name: Release 100 | uses: softprops/action-gh-release@v1 101 | if: startsWith(github.ref, 'refs/tags/') 102 | with: 103 | files: dotnet-cnblogs/** 104 | generate_release_notes: true -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Utils/TeaHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Dotnetcnblog.Utils 4 | { 5 | /// 6 | /// TEA(Tiny Encryption Algorithm)是一种小型的对称加密解密算法,支持128位密码, 7 | /// 与BlowFish一样TEA每次只能加密/解密8字节数据。TEA特点是速度快、效率高,实现也 8 | /// 非常简单。由于针对TEA的攻击不断出现,所以TEA也发展出几个版本,分别是XTEA、Block TEA和XXTEA。 9 | /// Copy from http://www.cnblogs.com/linzheng/archive/2011/09/14/2176767.html 10 | /// 11 | public class TeaHelper 12 | { 13 | /// 14 | /// 加密 15 | /// 16 | /// 数据 17 | /// key 1-16 character 18 | /// 19 | public static byte[] Encrypt(byte[] data, byte[] key) 20 | { 21 | byte[] dataBytes; 22 | if (data.Length % 2 == 0) 23 | { 24 | dataBytes = data; 25 | } 26 | else 27 | { 28 | dataBytes = new byte[data.Length + 1]; 29 | Array.Copy(data, 0, dataBytes, 0, data.Length); 30 | dataBytes[data.Length] = 0x0; 31 | } 32 | 33 | var result = new byte[dataBytes.Length * 4]; 34 | var formattedKey = FormatKey(key); 35 | var tempData = new uint[2]; 36 | for (var i = 0; i < dataBytes.Length; i += 2) 37 | { 38 | tempData[0] = dataBytes[i]; 39 | tempData[1] = dataBytes[i + 1]; 40 | Encrypt(tempData, formattedKey); 41 | Array.Copy(ConvertUIntToByteArray(tempData[0]), 0, result, i * 4, 4); 42 | Array.Copy(ConvertUIntToByteArray(tempData[1]), 0, result, i * 4 + 4, 4); 43 | } 44 | 45 | return result; 46 | } 47 | 48 | /// 49 | /// 解密 50 | /// 51 | /// 数据 52 | /// 密钥 1-16 字符 53 | /// 54 | public static byte[] Decrypt(byte[] data, byte[] key) 55 | { 56 | var formattedKey = FormatKey(key); 57 | var x = 0; 58 | var tempData = new uint[2]; 59 | var dataBytes = new byte[data.Length / 8 * 2]; 60 | for (var i = 0; i < data.Length; i += 8) 61 | { 62 | tempData[0] = ConvertByteArrayToUInt(data, i); 63 | tempData[1] = ConvertByteArrayToUInt(data, i + 4); 64 | Decode(tempData, formattedKey); 65 | dataBytes[x++] = (byte) tempData[0]; 66 | dataBytes[x++] = (byte) tempData[1]; 67 | } 68 | 69 | //修剪添加的空字符 70 | if (dataBytes[dataBytes.Length - 1] == 0x0) 71 | { 72 | var result = new byte[dataBytes.Length - 1]; 73 | Array.Copy(dataBytes, 0, result, 0, dataBytes.Length - 1); 74 | return result; 75 | } 76 | 77 | return dataBytes; 78 | } 79 | 80 | private static uint[] FormatKey(byte[] key) 81 | { 82 | if (key.Length == 0) 83 | throw new ArgumentException("Key must be between 1 and 16 characters in length"); 84 | var refineKey = new byte[16]; 85 | if (key.Length < 16) 86 | { 87 | Array.Copy(key, 0, refineKey, 0, key.Length); 88 | for (var k = key.Length; k < 16; k++) refineKey[k] = 0x20; 89 | } 90 | else 91 | { 92 | Array.Copy(key, 0, refineKey, 0, 16); 93 | } 94 | 95 | var formattedKey = new uint[4]; 96 | var j = 0; 97 | for (var i = 0; i < refineKey.Length; i += 4) 98 | formattedKey[j++] = ConvertByteArrayToUInt(refineKey, i); 99 | return formattedKey; 100 | } 101 | 102 | private static byte[] ConvertUIntToByteArray(uint v) 103 | { 104 | var result = new byte[4]; 105 | result[0] = (byte) (v & 0xFF); 106 | result[1] = (byte) ((v >> 8) & 0xFF); 107 | result[2] = (byte) ((v >> 16) & 0xFF); 108 | result[3] = (byte) ((v >> 24) & 0xFF); 109 | return result; 110 | } 111 | 112 | private static uint ConvertByteArrayToUInt(byte[] v, int offset) 113 | { 114 | if (offset + 4 > v.Length) return 0; 115 | uint output; 116 | output = v[offset]; 117 | output |= (uint) (v[offset + 1] << 8); 118 | output |= (uint) (v[offset + 2] << 16); 119 | output |= (uint) (v[offset + 3] << 24); 120 | return output; 121 | } 122 | 123 | #region Tea Algorithm 124 | 125 | private static void Encrypt(uint[] v, uint[] k) 126 | { 127 | var y = v[0]; 128 | var z = v[1]; 129 | uint sum = 0; 130 | var delta = 0x9e3779b9; 131 | uint n = 16; 132 | while (n-- > 0) 133 | { 134 | sum += delta; 135 | y += ((z << 4) + k[0]) ^ (z + sum) ^ ((z >> 5) + k[1]); 136 | z += ((y << 4) + k[2]) ^ (y + sum) ^ ((y >> 5) + k[3]); 137 | } 138 | 139 | v[0] = y; 140 | v[1] = z; 141 | } 142 | 143 | private static void Decode(uint[] v, uint[] k) 144 | { 145 | uint n = 16; 146 | uint sum; 147 | var y = v[0]; 148 | var z = v[1]; 149 | var delta = 0x9e3779b9; 150 | /* 151 | * 由于进行16轮运算,所以将delta左移4位,减16次后刚好为0. 152 | */ 153 | sum = delta << 4; 154 | while (n-- > 0) 155 | { 156 | z -= ((y << 4) + k[2]) ^ (y + sum) ^ ((y >> 5) + k[3]); 157 | y -= ((z << 4) + k[0]) ^ (z + sum) ^ ((z >> 5) + k[1]); 158 | sum -= delta; 159 | } 160 | 161 | v[0] = y; 162 | v[1] = z; 163 | } 164 | 165 | #endregion 166 | } 167 | } -------------------------------------------------------------------------------- /src/dotnet-cnblogs/Utils/MimeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | namespace Dotnetcnblog.Utils 5 | { 6 | public class MimeMapping 7 | { 8 | private static readonly Hashtable MimeMappingTable; 9 | 10 | static MimeMapping() 11 | { 12 | MimeMappingTable = new Hashtable(190, StringComparer.CurrentCultureIgnoreCase); 13 | AddMimeMapping(".323", "text/h323"); 14 | AddMimeMapping(".asx", "video/x-ms-asf"); 15 | AddMimeMapping(".acx", "application/internet-property-stream"); 16 | AddMimeMapping(".ai", "application/postscript"); 17 | AddMimeMapping(".aif", "audio/x-aiff"); 18 | AddMimeMapping(".aiff", "audio/aiff"); 19 | AddMimeMapping(".axs", "application/olescript"); 20 | AddMimeMapping(".aifc", "audio/aiff"); 21 | AddMimeMapping(".asr", "video/x-ms-asf"); 22 | AddMimeMapping(".avi", "video/x-msvideo"); 23 | AddMimeMapping(".asf", "video/x-ms-asf"); 24 | AddMimeMapping(".au", "audio/basic"); 25 | AddMimeMapping(".application", "application/x-ms-application"); 26 | AddMimeMapping(".bin", "application/octet-stream"); 27 | AddMimeMapping(".bas", "text/plain"); 28 | AddMimeMapping(".bcpio", "application/x-bcpio"); 29 | AddMimeMapping(".bmp", "image/bmp"); 30 | AddMimeMapping(".cdf", "application/x-cdf"); 31 | AddMimeMapping(".cat", "application/vndms-pkiseccat"); 32 | AddMimeMapping(".crt", "application/x-x509-ca-cert"); 33 | AddMimeMapping(".c", "text/plain"); 34 | AddMimeMapping(".css", "text/css"); 35 | AddMimeMapping(".cer", "application/x-x509-ca-cert"); 36 | AddMimeMapping(".crl", "application/pkix-crl"); 37 | AddMimeMapping(".cmx", "image/x-cmx"); 38 | AddMimeMapping(".csh", "application/x-csh"); 39 | AddMimeMapping(".cod", "image/cis-cod"); 40 | AddMimeMapping(".cpio", "application/x-cpio"); 41 | AddMimeMapping(".clp", "application/x-msclip"); 42 | AddMimeMapping(".crd", "application/x-mscardfile"); 43 | AddMimeMapping(".deploy", "application/octet-stream"); 44 | AddMimeMapping(".dll", "application/x-msdownload"); 45 | AddMimeMapping(".dot", "application/msword"); 46 | AddMimeMapping(".doc", "application/msword"); 47 | AddMimeMapping(".dvi", "application/x-dvi"); 48 | AddMimeMapping(".dir", "application/x-director"); 49 | AddMimeMapping(".dxr", "application/x-director"); 50 | AddMimeMapping(".der", "application/x-x509-ca-cert"); 51 | AddMimeMapping(".dib", "image/bmp"); 52 | AddMimeMapping(".dcr", "application/x-director"); 53 | AddMimeMapping(".disco", "text/xml"); 54 | AddMimeMapping(".exe", "application/octet-stream"); 55 | AddMimeMapping(".etx", "text/x-setext"); 56 | AddMimeMapping(".evy", "application/envoy"); 57 | AddMimeMapping(".eml", "message/rfc822"); 58 | AddMimeMapping(".eps", "application/postscript"); 59 | AddMimeMapping(".flr", "x-world/x-vrml"); 60 | AddMimeMapping(".fif", "application/fractals"); 61 | AddMimeMapping(".gtar", "application/x-gtar"); 62 | AddMimeMapping(".gif", "image/gif"); 63 | AddMimeMapping(".gz", "application/x-gzip"); 64 | AddMimeMapping(".hta", "application/hta"); 65 | AddMimeMapping(".htc", "text/x-component"); 66 | AddMimeMapping(".htt", "text/webviewhtml"); 67 | AddMimeMapping(".h", "text/plain"); 68 | AddMimeMapping(".hdf", "application/x-hdf"); 69 | AddMimeMapping(".hlp", "application/winhlp"); 70 | AddMimeMapping(".html", "text/html"); 71 | AddMimeMapping(".htm", "text/html"); 72 | AddMimeMapping(".hqx", "application/mac-binhex40"); 73 | AddMimeMapping(".isp", "application/x-internet-signup"); 74 | AddMimeMapping(".iii", "application/x-iphone"); 75 | AddMimeMapping(".ief", "image/ief"); 76 | AddMimeMapping(".ivf", "video/x-ivf"); 77 | AddMimeMapping(".ins", "application/x-internet-signup"); 78 | AddMimeMapping(".ico", "image/x-icon"); 79 | AddMimeMapping(".jpg", "image/jpeg"); 80 | AddMimeMapping(".jfif", "image/pjpeg"); 81 | AddMimeMapping(".jpe", "image/jpeg"); 82 | AddMimeMapping(".jpeg", "image/jpeg"); 83 | AddMimeMapping(".js", "application/x-javascript"); 84 | AddMimeMapping(".lsx", "video/x-la-asf"); 85 | AddMimeMapping(".latex", "application/x-latex"); 86 | AddMimeMapping(".lsf", "video/x-la-asf"); 87 | AddMimeMapping(".manifest", "application/x-ms-manifest"); 88 | AddMimeMapping(".mhtml", "message/rfc822"); 89 | AddMimeMapping(".mny", "application/x-msmoney"); 90 | AddMimeMapping(".mht", "message/rfc822"); 91 | AddMimeMapping(".mid", "audio/mid"); 92 | AddMimeMapping(".mpv2", "video/mpeg"); 93 | AddMimeMapping(".man", "application/x-troff-man"); 94 | AddMimeMapping(".mvb", "application/x-msmediaview"); 95 | AddMimeMapping(".mpeg", "video/mpeg"); 96 | AddMimeMapping(".m3u", "audio/x-mpegurl"); 97 | AddMimeMapping(".mdb", "application/x-msaccess"); 98 | AddMimeMapping(".mpp", "application/vnd.ms-project"); 99 | AddMimeMapping(".m1v", "video/mpeg"); 100 | AddMimeMapping(".mpa", "video/mpeg"); 101 | AddMimeMapping(".me", "application/x-troff-me"); 102 | AddMimeMapping(".m13", "application/x-msmediaview"); 103 | AddMimeMapping(".movie", "video/x-sgi-movie"); 104 | AddMimeMapping(".m14", "application/x-msmediaview"); 105 | AddMimeMapping(".mpe", "video/mpeg"); 106 | AddMimeMapping(".mp2", "video/mpeg"); 107 | AddMimeMapping(".mov", "video/quicktime"); 108 | AddMimeMapping(".mp3", "audio/mpeg"); 109 | AddMimeMapping(".mpg", "video/mpeg"); 110 | AddMimeMapping(".ms", "application/x-troff-ms"); 111 | AddMimeMapping(".nc", "application/x-netcdf"); 112 | AddMimeMapping(".nws", "message/rfc822"); 113 | AddMimeMapping(".oda", "application/oda"); 114 | AddMimeMapping(".ods", "application/oleobject"); 115 | AddMimeMapping(".pmc", "application/x-perfmon"); 116 | AddMimeMapping(".p7r", "application/x-pkcs7-certreqresp"); 117 | AddMimeMapping(".p7b", "application/x-pkcs7-certificates"); 118 | AddMimeMapping(".p7s", "application/pkcs7-signature"); 119 | AddMimeMapping(".pmw", "application/x-perfmon"); 120 | AddMimeMapping(".ps", "application/postscript"); 121 | AddMimeMapping(".p7c", "application/pkcs7-mime"); 122 | AddMimeMapping(".pbm", "image/x-portable-bitmap"); 123 | AddMimeMapping(".ppm", "image/x-portable-pixmap"); 124 | AddMimeMapping(".pub", "application/x-mspublisher"); 125 | AddMimeMapping(".pnm", "image/x-portable-anymap"); 126 | AddMimeMapping(".png", "image/png"); 127 | AddMimeMapping(".pml", "application/x-perfmon"); 128 | AddMimeMapping(".p10", "application/pkcs10"); 129 | AddMimeMapping(".pfx", "application/x-pkcs12"); 130 | AddMimeMapping(".p12", "application/x-pkcs12"); 131 | AddMimeMapping(".pdf", "application/pdf"); 132 | AddMimeMapping(".pps", "application/vnd.ms-powerpoint"); 133 | AddMimeMapping(".p7m", "application/pkcs7-mime"); 134 | AddMimeMapping(".pko", "application/vndms-pkipko"); 135 | AddMimeMapping(".ppt", "application/vnd.ms-powerpoint"); 136 | AddMimeMapping(".pmr", "application/x-perfmon"); 137 | AddMimeMapping(".pma", "application/x-perfmon"); 138 | AddMimeMapping(".pot", "application/vnd.ms-powerpoint"); 139 | AddMimeMapping(".prf", "application/pics-rules"); 140 | AddMimeMapping(".pgm", "image/x-portable-graymap"); 141 | AddMimeMapping(".qt", "video/quicktime"); 142 | AddMimeMapping(".ra", "audio/x-pn-realaudio"); 143 | AddMimeMapping(".rgb", "image/x-rgb"); 144 | AddMimeMapping(".ram", "audio/x-pn-realaudio"); 145 | AddMimeMapping(".rmi", "audio/mid"); 146 | AddMimeMapping(".ras", "image/x-cmu-raster"); 147 | AddMimeMapping(".roff", "application/x-troff"); 148 | AddMimeMapping(".rtf", "application/rtf"); 149 | AddMimeMapping(".rtx", "text/richtext"); 150 | AddMimeMapping(".sv4crc", "application/x-sv4crc"); 151 | AddMimeMapping(".spc", "application/x-pkcs7-certificates"); 152 | AddMimeMapping(".setreg", "application/set-registration-initiation"); 153 | AddMimeMapping(".snd", "audio/basic"); 154 | AddMimeMapping(".stl", "application/vndms-pkistl"); 155 | AddMimeMapping(".setpay", "application/set-payment-initiation"); 156 | AddMimeMapping(".stm", "text/html"); 157 | AddMimeMapping(".shar", "application/x-shar"); 158 | AddMimeMapping(".sh", "application/x-sh"); 159 | AddMimeMapping(".sit", "application/x-stuffit"); 160 | AddMimeMapping(".spl", "application/futuresplash"); 161 | AddMimeMapping(".sct", "text/scriptlet"); 162 | AddMimeMapping(".scd", "application/x-msschedule"); 163 | AddMimeMapping(".sst", "application/vndms-pkicertstore"); 164 | AddMimeMapping(".src", "application/x-wais-source"); 165 | AddMimeMapping(".sv4cpio", "application/x-sv4cpio"); 166 | AddMimeMapping(".tex", "application/x-tex"); 167 | AddMimeMapping(".tgz", "application/x-compressed"); 168 | AddMimeMapping(".t", "application/x-troff"); 169 | AddMimeMapping(".tar", "application/x-tar"); 170 | AddMimeMapping(".tr", "application/x-troff"); 171 | AddMimeMapping(".tif", "image/tiff"); 172 | AddMimeMapping(".txt", "text/plain"); 173 | AddMimeMapping(".texinfo", "application/x-texinfo"); 174 | AddMimeMapping(".trm", "application/x-msterminal"); 175 | AddMimeMapping(".tiff", "image/tiff"); 176 | AddMimeMapping(".tcl", "application/x-tcl"); 177 | AddMimeMapping(".texi", "application/x-texinfo"); 178 | AddMimeMapping(".tsv", "text/tab-separated-values"); 179 | AddMimeMapping(".ustar", "application/x-ustar"); 180 | AddMimeMapping(".uls", "text/iuls"); 181 | AddMimeMapping(".vcf", "text/x-vcard"); 182 | AddMimeMapping(".wps", "application/vnd.ms-works"); 183 | AddMimeMapping(".wav", "audio/wav"); 184 | AddMimeMapping(".wrz", "x-world/x-vrml"); 185 | AddMimeMapping(".wri", "application/x-mswrite"); 186 | AddMimeMapping(".wks", "application/vnd.ms-works"); 187 | AddMimeMapping(".wmf", "application/x-msmetafile"); 188 | AddMimeMapping(".wcm", "application/vnd.ms-works"); 189 | AddMimeMapping(".wrl", "x-world/x-vrml"); 190 | AddMimeMapping(".wdb", "application/vnd.ms-works"); 191 | AddMimeMapping(".wsdl", "text/xml"); 192 | AddMimeMapping(".xap", "application/x-silverlight-app"); 193 | AddMimeMapping(".xml", "text/xml"); 194 | AddMimeMapping(".xlm", "application/vnd.ms-excel"); 195 | AddMimeMapping(".xaf", "x-world/x-vrml"); 196 | AddMimeMapping(".xla", "application/vnd.ms-excel"); 197 | AddMimeMapping(".xls", "application/vnd.ms-excel"); 198 | AddMimeMapping(".xof", "x-world/x-vrml"); 199 | AddMimeMapping(".xlt", "application/vnd.ms-excel"); 200 | AddMimeMapping(".xlc", "application/vnd.ms-excel"); 201 | AddMimeMapping(".xsl", "text/xml"); 202 | AddMimeMapping(".xbm", "image/x-xbitmap"); 203 | AddMimeMapping(".xlw", "application/vnd.ms-excel"); 204 | AddMimeMapping(".xpm", "image/x-xpixmap"); 205 | AddMimeMapping(".xwd", "image/x-xwindowdump"); 206 | AddMimeMapping(".xsd", "text/xml"); 207 | AddMimeMapping(".z", "application/x-compress"); 208 | AddMimeMapping(".zip", "application/x-zip-compressed"); 209 | AddMimeMapping(".*", "application/octet-stream"); 210 | } 211 | 212 | private static void AddMimeMapping(string extension, string mimeType) 213 | { 214 | MimeMappingTable.Add(extension, mimeType); 215 | } 216 | 217 | public static string GetMimeMapping(string fileName) 218 | { 219 | string text = null; 220 | var num = fileName.LastIndexOf('.'); 221 | if (0 < num && num > fileName.LastIndexOf('\\')) text = (string) MimeMappingTable[fileName.Substring(num)]; 222 | if (text == null) text = (string) MimeMappingTable[".*"]; 223 | return text; 224 | } 225 | } 226 | } --------------------------------------------------------------------------------