1. 程式人生 > >ASP.NET Core Web API + Angular 仿B站(二)後臺模型創建以及數據庫的初始化

ASP.NET Core Web API + Angular 仿B站(二)後臺模型創建以及數據庫的初始化

[] let 查看 hang lar enc cep 連接字符串 nds

前言:

本系列文章主要為對所學 Angular 框架的一次微小的實踐,對 b站頁面作簡單的模仿。

本系列文章主要參考資料:

微軟文檔:    https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/?view=aspnetcore-2.1&tabs=windows

Angular 文檔:   https://angular.cn/tutorial

Typescript 文檔: https://www.typescriptlang.org/docs/home.html

此系列皆使用 C#+Typescript+Angular+EF Core 作為開發環境,使用 VSCode 對 Angular 進行開發同時作為命令行啟動器,使用 VS2017 對 ASP.NET Core 進行開發。如果有什麽問題或者意見歡迎在留言區進行留言。

如果圖片看不清的話請在新窗口打開圖片或保存本地查看。

項目 github 地址:https://github.com/NanaseRuri/FakeBilibili

本章內容:後臺模型分析以及建立數據庫

一、模型分析

技術分享圖片

二、創建模型以及數據庫

用戶登錄信息:

通過添加修飾 [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)] 可以手動設置 Id 屬性插入數據庫:

增加一個 salt 用來接受隨機字符串以進行加密。

 1     public class UserIdentity
 2     {
 3         [Key]
 4         [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)]
 5         public int Id { get; set; }
 6 
 7         [Required]
 8         public string UserName { get; set; }
 9 
10         [Required]
11 [EmailAddress] 12 public string Email { get; set; } 13 14 [Required] 15 public string Password { get; set; } 16 17 [Required] 18 public string Salt { get; set; } 19 }

用戶登錄信息數據庫,通過創建索引的方式為用戶名和郵箱添加唯一約束:

 1     public class UserIdentityDbContext : DbContext
 2     {
 3         public UserIdentityDbContext(DbContextOptions<UserIdentityDbContext> options):base(options)
 4         { }
 5 
 6         public DbSet<UserIdentity> Users { get; set; }
 7 
 8         protected override void OnModelCreating(ModelBuilder modelBuilder)
 9         {
10             modelBuilder.Entity<UserIdentity>().HasIndex(u => new { u.Email }).IsUnique(true);
11             modelBuilder.Entity<UserIdentity>().HasIndex(u => new { u.UserName }).IsUnique(true);
12         }
13     }

在 appsetting.json 中添加數據庫連接字符串:

1   "ConnectionStrings": {
2     "UserIdentityDbContext": "Server=(localdb)\\mssqllocaldb;Database=UserIdentityDbContext;Trusted_Connection=True;MultipleActiveResultSets=true",
3     "UserAndVideoDbContext": "Server=(localdb)\\mssqllocaldb;Database=UserAndVideoDbContext;Trusted_Connection=True;MultipleActiveResultSets=true"
4   },

VS2017 PM控制臺中添加遷移:

    add-migration UserIdentityDb -c FakeBilibili.Data.UserIdentityDbContext

技術分享圖片

添加數據庫:

    update-database -c FakeBilibili.Data.UserIdentityDbContext

技術分享圖片

查看數據庫中 User 表的定義,已添加唯一約束:

技術分享圖片

用戶信息類:

 1     public class User
 2     {
 3         [Key]
 4         [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)]
 5         public int Id { get; set; }
 6 
 7         [EmailAddress]
 8         [Required]
 9         public string Email { get; set; }
10 
11         [Required]
12         public string UserName { get; set; }
13 
14         public string AvatarLocation { get; set; }
15 
16         /// <summary>
17         /// 作品
18         /// </summary>
19         public ICollection<Video> Works { get; set; }
20 
21         /// <summary>
22         /// 關註,內部用 / 分隔
23         /// </summary>
24         public string Follows { get; set; }
25 
26         /// <summary>
27         /// 粉絲
28         /// </summary>
29         public string Fans { get; set; }
30     }

首先新建枚舉用於確定視頻分類:

1     public enum Category
2     {
3         動畫,
4         番劇,
5         音樂,
6         數碼,
7         遊戲,
8         科技
9     }

視頻信息類:

 1     public class Video
 2     {
 3         [Key]
 4         public int Id { get; set; }
 5 
 6         [Required]
 7         public string Title { get; set; }
 8 
 9         [Required]
10         public User Author { get; set; }
11 
12         [Required]
13         public string VideoLocation { get; set; }     
14         
15         [Required]
16         public string ThumbnailLocation { get; set; }       
17 
18         [Required]
19         public TimeSpan Duration { get; set; }
20 
21         [Required]
22         public DateTime PublishDateTime { get; set; }
23 
24         /// <summary>
25         /// 類別
26         /// </summary>
27         [Required]
28         public Category Category { get; set; }
29 
30         /// <summary>
31         /// 標簽
32         /// </summary>
33         public string Tag { get; set; }
34 
35         /// <summary>
36         /// 觀看數
37         /// </summary>
38         [Required]
39         public int VideoView { get; set; }
40     }

創建用戶信息和視頻的數據庫,並通過創建索引的方式為用戶名和郵箱添加唯一約束:

 1     public class UserAndVideoDbContext:DbContext
 2     {
 3         public UserAndVideoDbContext(DbContextOptions<UserAndVideoDbContext> options):base(options) { }
 4 
 5         public DbSet<User> Users { get; set; }
 6         public DbSet<Video> Videos { get; set; }
 7 
 8         protected override void OnModelCreating(ModelBuilder modelBuilder)
 9         {
10             modelBuilder.Entity<User>().HasIndex(u => new { u.Email}).IsUnique(true);
11             modelBuilder.Entity<User>().HasIndex(u => new { u.UserName }).IsUnique(true);
12         }
13     }

運行遷移和更新命令:

1     add-migration UserAndVideoDb -c FakeBilibili.Data.UserAndVideoDbContext
2     update-database -c FakeBilibili.Data.UserAndVideoDbContext

技術分享圖片

三、數據庫初始化

為了方便添加鹽值,添加一個 SaltGenerator 類:

默認生成一個 8 位的隨機字符串:

 1     public class SaltGenerator
 2     {
 3         public static string GenerateSalt()
 4         {
 5             return GenerateSalt(8);
 6         }
 7 
 8         public static string GenerateSalt(int length)
 9         {
10             if (length<=0)
11             {
12                 return String.Empty;
13             }
14 
15             StringBuilder salt = new StringBuilder();
16             Random random=new Random();            
17             StringBuilder saltCharList=new StringBuilder();
18             //將小寫字母加入到列表中
19             for (int i = 97; i <= 122; i++)
20             {
21                 saltCharList.Append((char) i);
22             }
23             //將大寫字母加入到列表中
24             for (int i = 65; i <=90; i++)
25             {
26                 saltCharList.Append((char) i);
27             }
28             saltCharList.Append(0123456789);
29 
30             for (int saltIndex = 0; saltIndex < length; saltIndex++)
31             {
32                 salt.Append(saltCharList[random.Next(61)]);
33             }
34 
35             return salt.ToString();
36         }
37     }

為了方便對密碼進行加密,在此創建一個 Encryptor 類:

 1     public class Encryptor : IEncrypt
 2     {
 3         private readonly SHA256 sha256;
 4 
 5         public Encryptor()
 6         {
 7             sha256 = SHA256.Create();
 8         }
 9 
10         public string Encrypt(string password, string salt)
11         {
12             byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password + salt));
13             StringBuilder hashPassword = new StringBuilder();
14             foreach (var hashByte in hashBytes)
15             {
16                 hashPassword.Append(hashByte);
17             }
18             return hashPassword.ToString();
19         }
20     }

分別創建三個類進行登錄用用戶,用戶以及視頻的初始化:

對登錄用用戶進行初始化,使用 SHA256 算法對密碼進行加密:

 1     public class UserIdentityInitiator
 2     {
 3         public static async Task Initial(IServiceProvider provider)
 4         {
 5             UserIdentityDbContext context = provider.GetRequiredService<UserIdentityDbContext>();
 6             Encryptor encryptor = new Encryptor();
 7             if (!context.Users.Any())
 8             {                
 9                 for (int i = 0; i < 20; i++)
10                 {
11                     string salt = SaltGenerator.GenerateSalt();
12                     UserIdentity user = new UserIdentity()
13                     {
14                         UserName = $"User{i+1}",                        
15                         Password = encryptor.Encrypt($"User{i+1}",salt),
16                         Salt = salt,
17                         Id = i+1,
18                         Email = $"User{i + 1}@cnblog.com"
19                     };
20                     await context.Users.AddAsync(user);
21                     await context.SaveChangesAsync();
22                 }
23             }
24         }
25     }

然後在 Configure 方法最後一行中添加代碼:

             UserIdentityInitiator.Initial(app.ApplicationServices).Wait();

運行程序後查看本地數據庫:

技術分享圖片

對用戶進行初始化:

在此添加了一個 Avatar 文件夾並放入幾張圖片備用,對用戶頭像進行初始化:

技術分享圖片

 1     public class UserInitiator
 2     {
 3         public static async Task Initial(IServiceProvider serviceProvider)
 4         {
 5             UserAndVideoDbContext context = serviceProvider.GetRequiredService<UserAndVideoDbContext>();
 6             if (!context.Users.Any())
 7             {
 8                 string currentDirectory = Directory.GetCurrentDirectory();
 9                 int pictureSerial = 0;                  
10                                 
11                 for (int i = 0; i < 20; i++)
12                 {
13                     pictureSerial = i % 4;
14                     User user = new User()
15                     {
16                         AvatarLocation = Path.Combine(currentDirectory,"Avatar",$"{pictureSerial}.jpg"),
17                         UserName = $"User{i+1}",
18                         Id = i+1,
19                         Email = $"User{i+1}@cnblog.com"
20                     };                    
21 
22                     await context.Users.AddAsync(user);
23                     await context.SaveChangesAsync();
24                 }
25             }
26         }
27     }

在 Configure 方法最後一行添加方法:

            UserInitiator.InitialUsers(app.ApplicationServices).Wait();

技術分享圖片

對視頻進行初始化:

新建一個 Video 文件夾放置若幹視頻:

技術分享圖片

在.net core 中為了生成縮略圖,需要引用 System.Drawing.Common 庫:

技術分享圖片

為了方便地獲取視頻封面,可以引用 Xabe.FFmpeg 庫:

技術分享圖片

由於 Xabe.FFmpeg 是 .NET 平臺上對 FFmpeg 封裝,基本功能依靠於 FFmpeg 實現,因此需要下載 FFmpeg:https://ffmpeg.zeranoe.com/builds/

技術分享圖片

解壓文件後將文件夾添加到環境變量中:

技術分享圖片

新建一個類用以在本地保存縮略圖:

1     public class PictureTrimmer
2     {        
3         public static string GetLocalTrimmedPicture(string fileName)
4         {
5             string newLocation = fileName.Insert(fileName.LastIndexOf(".")+1, "min.");
6             Image.FromFile(fileName).GetThumbnailImage(320, 180, (() => false), IntPtr.Zero).Save(newLocation);
7             return newLocation;
8         }
9     }

初始化視頻:

為 FFmpeg.ExecutablesPath 賦值,設置成 FFmpeg 解壓後的位置:

由於 Xabe.FFmpeg 創建圖片時使用 FileStream 指定 FileMode 為 NewCreate,所以存在已有文件時會拋出異常,需保證創建的圖片不與現有文件重名。

在此全部視頻由 ID 為1的用戶上傳:

 1     public class VideoInitiator
 2     {
 3         public static async Task Initial(IServiceProvider provider)
 4         {
 5             FFmpeg.ExecutablesPath = @"D:\office softwares\FFMpeg";
 6 
 7             UserAndVideoDbContext context = provider.GetRequiredService<UserAndVideoDbContext>();
 8             string videoDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Video");
 9 
10             User author = context.Users.Include(u => u.Works).FirstOrDefault(u => u.Id == 1);
11 
12             if (!context.Videos.Any())
13             {
14                 for (int i = 1; i <= 6; i++)
15                 {
16                     string videoPath = Path.Combine(videoDirectory, $"{i}.mp4");
17                     string picPath = Path.Combine(videoDirectory, $"{i}.jpg");
18 
19                     if (File.Exists(picPath))
20                     {
21                         File.Delete(picPath);
22                     }
23 
24                     //獲取視頻信息
25                     IMediaInfo mediaInfo = await MediaInfo.Get(videoPath);
26                     //以 0 秒時的畫面作為封面圖並保存在本地
27                     Conversion.Snapshot(videoPath, picPath,
28                         TimeSpan.FromSeconds(0)).Start().Wait();
29 
30                     Video video = new Video()
31                     {
32                         Title = $"輕音少女 第{i}集",
33                         Author = context.Users.FirstOrDefault(u => u.Id == 0),
34                         Category = Category.番劇,
35                         VideoLocation = videoPath,
36                         Duration = mediaInfo.Duration,
37                         PublishDateTime = DateTime.Now,
38                         ThumbnailLocation = PictureTrimmer.GetLocalTrimmedPicture(picPath),
39                         Tag = "輕音少女",
40                         VideoView = 0
41                     };
42                     author.Works.Add(video);
43                     await context.Videos.AddAsync(video);
44                     await context.SaveChangesAsync();
45                 }
46 
47             }
48         }
49     }

在 Configure 方法最後一行調用初始化方法:

            VideoInitiator.Initial(app.ApplicationServices).Wait();

運行程序:

技術分享圖片

至此數據庫初始化工作完成。

ASP.NET Core Web API + Angular 仿B站(二)後臺模型創建以及數據庫的初始化