前言:

本系列文章主要为对所学 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

本章内容:后台模型创建以及数据库的初始化,ASP.NET Core,获取缩略图,获取视频某一帧图片,ffmpeg,Image,SHA256,加密,盐,EntityFramework 唯一性约束

一、模型分析

二、创建模型以及数据库

用户登录信息:

通过添加修饰 [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)] 可以手动设置 Id 属性插入数据库:

增加一个 salt 用来接受随机字符串以进行加密。

     public class UserIdentity
     {
         [Key]
         [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)]
         public int Id { get; set; }

         [Required]
         public string UserName { get; set; }

         [Required]
         [EmailAddress]
         public string Email { get; set; }

         [Required]
         public string Password { get; set; }

         [Required]
         public string Salt { get; set; }
     }

用户登录信息数据库,通过创建索引的方式为用户名和邮箱添加唯一约束:

     public class UserIdentityDbContext : DbContext
     {
         public UserIdentityDbContext(DbContextOptions<UserIdentityDbContext> options):base(options)
         { }

         public DbSet<UserIdentity> Users { get; set; }

         protected override void OnModelCreating(ModelBuilder modelBuilder)
         {
             modelBuilder.Entity<UserIdentity>().HasIndex(u => new { u.Email }).IsUnique(true);
             modelBuilder.Entity<UserIdentity>().HasIndex(u => new { u.UserName }).IsUnique(true);
         }
     }

在 appsetting.json 中添加数据库连接字符串:

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

VS2017 PM控制台中添加迁移:

    add-migration UserIdentityDb -c FakeBilibili.Data.UserIdentityDbContext

添加数据库:

    update-database -c FakeBilibili.Data.UserIdentityDbContext

查看数据库中 User 表的定义,已添加唯一约束:

用户信息类:

     public class User
     {
         [Key]
         [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)]
         public int Id { get; set; }

         [EmailAddress]
         [Required]
         public string Email { get; set; }

         [Required]
         public string UserName { get; set; }

         public string AvatarLocation { get; set; }

         /// <summary>
         /// 作品
         /// </summary>
         public ICollection<Video> Works { get; set; }

         /// <summary>
         /// 关注,内部用 / 分隔
         /// </summary>
         public string Follows { get; set; }

         /// <summary>
         /// 粉丝
         /// </summary>
         public string Fans { get; set; }
     }

首先新建枚举用于确定视频分类:

     public enum Category
     {
         动画,
         番剧,
         音乐,
         数码,
         游戏,
         科技
     }

视频信息类:

     public class Video
     {
         [Key]
         public int Id { get; set; }

         [Required]
         public string Title { get; set; }

         [Required]
         public User Author { get; set; }

         [Required]
         public string VideoLocation { get; set; }     

         [Required]
         public string ThumbnailLocation { get; set; }       

         [Required]
         public TimeSpan Duration { get; set; }

         [Required]
         public DateTime PublishDateTime { get; set; }

         /// <summary>
         /// 类别
         /// </summary>
         [Required]
         public Category Category { get; set; }

         /// <summary>
         /// 标签
         /// </summary>
         public string Tag { get; set; }

         /// <summary>
         /// 观看数
         /// </summary>
         [Required]
         public int VideoView { get; set; }
     }

创建用户信息和视频的数据库,并通过创建索引的方式为用户名和邮箱添加唯一约束:

     public class UserAndVideoDbContext:DbContext
     {
         public UserAndVideoDbContext(DbContextOptions<UserAndVideoDbContext> options):base(options) { }

         public DbSet<User> Users { get; set; }
         public DbSet<Video> Videos { get; set; }

         protected override void OnModelCreating(ModelBuilder modelBuilder)
         {
             modelBuilder.Entity<User>().HasIndex(u => new { u.Email}).IsUnique(true);
             modelBuilder.Entity<User>().HasIndex(u => new { u.UserName }).IsUnique(true);
         }
     }

运行迁移和更新命令:

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

三、数据库初始化

为了方便添加盐值,添加一个 SaltGenerator 类:

默认生成一个 8 位的随机字符串:

     public class SaltGenerator
     {
         public static string GenerateSalt()
         {
             );
         }

         public static string GenerateSalt(int length)
         {
             )
             {
                 return String.Empty;
             }

             StringBuilder salt = new StringBuilder();
             Random random=new Random();
             StringBuilder saltCharList=new StringBuilder();
             //将小写字母加入到列表中
             ; i <= ; i++)
             {
                 saltCharList.Append((char) i);
             }
             //将大写字母加入到列表中
             ; i <=; i++)
             {
                 saltCharList.Append((char) i);
             }
             saltCharList.Append();

             ; saltIndex < length; saltIndex++)
             {
                 salt.Append(saltCharList[random.Next()]);
             }

             return salt.ToString();
         }
     }

为了方便对密码进行加密,在此创建一个 Encryptor 类:

     public class Encryptor : IEncrypt
     {
         private readonly SHA256 sha256;

         public Encryptor()
         {
             sha256 = SHA256.Create();
         }

         public string Encrypt(string password, string salt)
         {
             byte[] hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password + salt));
             StringBuilder hashPassword = new StringBuilder();
             foreach (var hashByte in hashBytes)
             {
                 hashPassword.Append(hashByte);
             }
             return hashPassword.ToString();
         }
     }

分别创建三个类进行登录用用户,用户以及视频的初始化:

对登录用用户进行初始化,使用 SHA256 算法对密码进行加密:

     public class UserIdentityInitiator
     {
         public static async Task Initial(IServiceProvider provider)
         {
             UserIdentityDbContext context = provider.GetRequiredService<UserIdentityDbContext>();
             Encryptor encryptor = new Encryptor();
             if (!context.Users.Any())
             {
                 ; i < ; i++)
                 {
                     string salt = SaltGenerator.GenerateSalt();
                     UserIdentity user = new UserIdentity()
                     {
                         UserName = $"User{i+1}",
                         Password = encryptor.Encrypt($"User{i+1}",salt),
                         Salt = salt,
                         Id = i+,
                         Email = $"User{i + 1}@cnblog.com"
                     };
                     await context.Users.AddAsync(user);
                     await context.SaveChangesAsync();
                 }
             }
         }
     }

然后在 Configure 方法最后一行中添加代码:

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

运行程序后查看本地数据库:

对用户进行初始化:

在此添加了一个 Avatar 文件夹并放入几张图片备用,对用户头像进行初始化:

     public class UserInitiator
     {
         public static async Task Initial(IServiceProvider serviceProvider)
         {
             UserAndVideoDbContext context = serviceProvider.GetRequiredService<UserAndVideoDbContext>();
             if (!context.Users.Any())
             {
                 string currentDirectory = Directory.GetCurrentDirectory();
                 ;                  

                 ; i < ; i++)
                 {
                     pictureSerial = i % ;
                     User user = new User()
                     {
                         AvatarLocation = Path.Combine(currentDirectory,"Avatar",$"{pictureSerial}.jpg"),
                         UserName = $"User{i+1}",
                         Id = i+,
                         Email = $"User{i+1}@cnblog.com"
                     };                    

                     await context.Users.AddAsync(user);
                     await context.SaveChangesAsync();
                 }
             }
         }
     }

在 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/

解压文件后将文件夹添加到环境变量中:

新建一个类用以在本地保存缩略图:

     public class PictureTrimmer
     {
         public static string GetLocalTrimmedPicture(string fileName)
         {
             , "min.");
             Image.FromFile(fileName).GetThumbnailImage(, , (() => false), IntPtr.Zero).Save(newLocation);
             return newLocation;
         }
     }

初始化视频:

为 FFmpeg.ExecutablesPath 赋值,设置成 FFmpeg 解压后的位置:

由于 Xabe.FFmpeg 创建图片时使用 FileStream 指定 FileMode 为 NewCreate,所以存在已有文件时会抛出异常,需保证创建的图片不与现有文件重名。

在此全部视频由 ID 为1的用户上传:

     public class VideoInitiator
     {
         public static async Task Initial(IServiceProvider provider)
         {
             FFmpeg.ExecutablesPath = @"D:\office softwares\FFMpeg";

             UserAndVideoDbContext context = provider.GetRequiredService<UserAndVideoDbContext>();
             string videoDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Video");

             User author = context.Users.Include(u => u.Works).FirstOrDefault(u => u.Id == );

             if (!context.Videos.Any())
             {
                 ; i <= ; i++)
                 {
                     string videoPath = Path.Combine(videoDirectory, $"{i}.mp4");
                     string picPath = Path.Combine(videoDirectory, $"{i}.jpg");

                     if (File.Exists(picPath))
                     {
                         File.Delete(picPath);
                     }

                     //获取视频信息
                     IMediaInfo mediaInfo = await MediaInfo.Get(videoPath);
                     //以 0 秒时的画面作为封面图并保存在本地
                     Conversion.Snapshot(videoPath, picPath,
                         TimeSpan.FromSeconds()).Start().Wait();

                     Video video = new Video()
                     {
                         Title = $"轻音少女 第{i}集",
                         Author = context.Users.FirstOrDefault(u => u.Id == ),
                         Category = Category.番剧,
                         VideoLocation = videoPath,
                         Duration = mediaInfo.Duration,
                         PublishDateTime = DateTime.Now,
                         ThumbnailLocation = PictureTrimmer.GetLocalTrimmedPicture(picPath),
                         Tag = "轻音少女",
                         VideoView =
                     };
                     author.Works.Add(video);
                     await context.Videos.AddAsync(video);
                     await context.SaveChangesAsync();
                 }

             }
         }
     }

在 Configure 方法最后一行调用初始化方法:

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

运行程序:

至此数据库初始化工作完成。

ASP.NET Core Web API + Angular 仿B站(二)后台模型创建以及数据库的初始化的更多相关文章

  1. ASP.NET Core Web API + Angular 仿B站(三)后台配置 JWT 的基于 token 的验证

    前言: 本系列文章主要为对所学 Angular 框架的一次微小的实践,对 b站页面作简单的模仿. 本系列文章主要参考资料: 微软文档: https://docs.microsoft.com/zh-cn ...

  2. ASP.NET Core Web API + Angular 仿B站(一) 目的分析以及创建 WebAPI + Angular7 项目

    前言: 本系列文章主要为对所学 Angular 框架的一次微小的实践,对 b站页面作简单的模仿. 本系列文章主要参考资料: 微软文档: https://docs.microsoft.com/zh-cn ...

  3. ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线

    在上文中,我们讨论了事件处理器中对象生命周期的问题,在进入新的讨论之前,首先让我们总结一下,我们已经实现了哪些内容.下面的类图描述了我们已经实现的组件及其之间的关系,貌似系统已经变得越来越复杂了. 其 ...

  4. 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线

    重温.NET下Assembly的加载过程   最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后 ...

  5. 在ASP.NET Core Web API中为RESTful服务增加对HAL的支持

    HAL(Hypertext Application Language,超文本应用语言)是一种RESTful API的数据格式风格,为RESTful API的设计提供了接口规范,同时也降低了客户端与服务 ...

  6. ASP.NET Core Web API 索引 (更新Identity Server 4 视频教程)

    GraphQL 使用ASP.NET Core开发GraphQL服务器 -- 预备知识(上) 使用ASP.NET Core开发GraphQL服务器 -- 预备知识(下) [视频] 使用ASP.NET C ...

  7. asp.net core web api 生成 swagger 文档

    asp.net core web api 生成 swagger 文档 Intro 在前后端分离的开发模式下,文档就显得比较重要,哪个接口要传哪些参数,如果一两个接口还好,口头上直接沟通好就可以了,如果 ...

  8. ASP.NET Core Web API中带有刷新令牌的JWT身份验证流程

    ASP.NET Core Web API中带有刷新令牌的JWT身份验证流程 翻译自:地址 在今年年初,我整理了有关将JWT身份验证与ASP.NET Core Web API和Angular一起使用的详 ...

  9. 使用 Swagger 自动生成 ASP.NET Core Web API 的文档、在线帮助测试文档(ASP.NET Core Web API 自动生成文档)

    对于开发人员来说,构建一个消费应用程序时去了解各种各样的 API 是一个巨大的挑战.在你的 Web API 项目中使用 Swagger 的 .NET Core 封装 Swashbuckle 可以帮助你 ...

随机推荐

  1. maven最小配置

    将参与项目开发的开发人员的用户名及邮箱捆绑在一起,在code review是更加方便的进行版本管控: 1.配置user,name和user,email命令: $ git config --global ...

  2. 转: CentOS 6 使用 yum 安装MongoDB及服务器端配置

    转: http://www.cnblogs.com/shanyou/archive/2012/07/14/2591838.html CentOS 6 使用 yum 安装MongoDB及服务器端配置   ...

  3. Lambda 表达式的演示样例-来源(MSDN)

    本文演示怎样在你的程序中使用 lambda 表达式. 有关 lambda 表达式的概述.请參阅 C++ 中的 Lambda 表达式. 有关 lambda 表达式结构的具体信息,请參阅 Lambda 表 ...

  4. 【转载】一致性哈希算法(consistent hashing)

    一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法,设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似.一致性哈希修正了CARP使用的简 单哈 ...

  5. 【手势交互】4. Kinect for XBox

    "You are the Controller",Kinect for Xbox的广告词.明白说明了Kinect体感的交互方式.作为一款集成了诸多先进视觉技术的自然交互设备,Kin ...

  6. Qt linux文件同步写入

    因为linux 系统机制问题,文件的创建和写入并不会直接写入硬盘.而是先写入缓存,当系统要关闭或须要时才写入硬盘.为防止突然掉电,应将缓存中的文件及时同步到硬盘上去. linux 下的sync 命令具 ...

  7. 使用ucontext组件实现的coroutine代码分析

    coroutine一般翻译过来就是协程,类似于线程可以切换,而跟线程是由操作系统调度器来实现切换不一样,协程由用户程序自己调度进行切换.我以前也看过协程相关的内容,但没有自己去实现过.最近搞OpenS ...

  8. Canvas坐标轴中的Y轴距离是X轴的两倍

    如题,相信很多人在初次玩canvas的时候会出现这样的情况,跟着教程走的情况下,诶 怎么画出来的东西,不怎么对劲啊,,,ԾㅂԾ,,!!!!!先上代码 <!DOCTYPE html> < ...

  9. MySQL(6)--复制,docker容器中

    MySQL5.7.11实现replication 启动两个安装好mysql的空的docker image ----------------- shell1  master $docker run -i ...

  10. nyoj 题目10 skiing —— 南阳oj

    题目信息例如以下: skiing 时间限制:3000 ms  |  内存限制:65535 KB 难度:5 描写叙述 Michael喜欢滑雪百这并不奇怪, 由于滑雪的确非常刺激.但是为了获得速度,滑的区 ...