1、为了降低web服务器的压力,申请了2台文件服务器,用来存放图片文件。但是两台文件服务器如何让程序自己选择呢?
于是我用了一个算法,思路如下:
从状态表筛选出可用的图片服务器集合记作C,并获取集合的总记录数N,
然后用随机函数产生一个随机数R1与N进行取余运算记作I=R1%N,则c[I]即为要保存图片服务器。
然后我开始设计两个表,一个是图片文件服务器表、一个是图片信息表。1对多的关系。
表1:ImageServerInfo 图片文件服务器表
表2:ImageInfo 图片信息表
表脚本入下:
USE [MyImageServer] GO /****** Object: Table [dbo].[ImageServerInfo] Script Date: 07/04/2020 21:17:15 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[ImageServerInfo]( [ServerId] [int] IDENTITY(1,1) NOT NULL, --服务器id [ServerName] [nvarchar](32) NOT NULL, --图片服务器名称 [ServerUrl] [nvarchar](100) NOT NULL, --图片服务器 [PicRootPath] [nvarchar](100) NOT NULL, --图片存储的物理路径 [MaxPicAmount] [int] NOT NULL, ---图片存储的上限 [CurPicAmount] [int] NOT NULL, --图片当前存储的数量 [FlgUsable] [bit] NOT NULL, ---图片服务器的状态 CONSTRAINT [PK_ImageServerInfo] PRIMARY KEY CLUSTERED ( [ServerId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO
----------------
----------------
----------------
----------------
USE [MyImageServer]
GO
/****** Object: Table [dbo].[ImageInfo] Script Date: 07/04/2020 21:16:57 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[ImageInfo](
[Id] [int] IDENTITY(1,1) NOT NULL, ---该具体的id
[ImageName] [nvarchar](100) NOT NULL, ---图片的路径名称
[ImageServerId] [int] NOT NULL, ---存储到哪台服务器的id
CONSTRAINT [PK_ImageInfo] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[ImageInfo] WITH CHECK ADD CONSTRAINT [FK_ImageInfo_ImageServerInfo] FOREIGN KEY([ImageServerId])
REFERENCES [dbo].[ImageServerInfo] ([ServerId])
GO
ALTER TABLE [dbo].[ImageInfo] CHECK CONSTRAINT [FK_ImageInfo_ImageServerInfo]
GO
表结构建立好之后,开始打开 Microsoft Visual Studio 软件,新建一个 ImageSystem 解决方案,这里用来模拟web服务器。
1、web层我建立的是MVC进行演示 ImageSystem.WebApp。
2、然后建立一个实体层ImageSystem.Model 用来引用 Model1.edmx 作为EntityFramework做数据库连接。
3、建立两个空Web,用来做图片文件服务器。命名为 ImageSystem.ImageServeOne、ImageSystem.ImageServeTwo
4、项目结构如图:
web应用服务器的Controllers层的HomeController代码如下:
using ImageSystem.Model; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Web; using System.Web.Mvc; namespace ImageSystem.WebApp.Controllers { public class HomeController : Controller { MyImageServerEntities db = new MyImageServerEntities(); // GET: Home public ActionResult Index() { return View(); } public ActionResult FileUpload() { HttpPostedFileBase file = Request.Files["fileUp"]; if (file != null) { string fileName = Path.GetFileName(file.FileName); string fileExt = Path.GetExtension(fileName); if (fileExt == ".jpg" || fileExt == ".png" || fileExt == ".gif") { //从状态表筛选出可用的图片服务器集合记作C,并获取集合的总记录数N,然后用随机函数产生一个随机数R1与N进行取余运算记作I=R1%N,则c[I]即为要保存图片服务器 var list=db.ImageServerInfo.Where(a => a.FlgUsable == true).ToList(); int count = list.Count(); Random random = new Random(); int r = random.Next(); int i = r % count; ImageServerInfo imageServiceInfo = list[i];//筛选出一个服务器 WebClient client = new WebClient(); string address = "http://" + imageServiceInfo.ServerUrl + "/FileUp.ashx?serverId=" + imageServiceInfo.ServerId + "&ext=" + fileExt; client.UploadData(address, StreamToByte(file.InputStream)); return Content("文件上传成功!"); } else { return Content("文件类型错误!!"); } } else { return Content("文件不能为空!!"); } } private byte[] StreamToByte(Stream inputStream) { byte[] buffer = new byte[inputStream.Length]; inputStream.Read(buffer, 0, buffer.Length); inputStream.Seek(0, SeekOrigin.Begin); return buffer; } public ActionResult ShowImage() { var list = db.ImageServerInfo.Where(a => a.FlgUsable == true).ToList(); ViewData["list"] = list; return View(); } } }
HomeController对应的view视图Index页面如下:
@{ Layout = null; } "viewport" content="width=device-width" />分布式图片上传
HomeController对应的view视图ShowImage页面如下:
这个页面是辅助查看上传的图片进行展示
@{ Layout = null; } @using ImageSystem.Model "viewport" content="width=device-width" />ShowImage @if (ViewData["list"] != null) { foreach (var ImageServerInfo in (List)ViewData["list"]) { foreach (var ImageInfo in ImageServerInfo.ImageInfo) { "@string.Format("http://{0}{1}",ImageServerInfo.ServerUrl,ImageInfo.ImageName)" alt="" width="200px" height="170px" />
@ImageServerInfo.ServerName @ImageServerInfo.ServerUrl @ImageInfo.ImageName @ImageInfo.ImageServerId
} } }
以上便是模拟的web服务器,下面插入Web图片文件服务器的代码,我只展示一台,另外一台除了ip端口不同,代码都一样的:
在图片文件服务器根目录建立images文件夹,然后新增一个FileUp.ashx一般处理程序,用来接收图片的字节数组,一般处理程序的代码如下:
using ImageSystem.Model; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Web; namespace ImageSystem.ImageServeTwo { ////// FileUp 的摘要说明 /// public class FileUp : IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain"; string ext = context.Request["ext"]; int serverId = int.Parse(context.Request["serverId"]); string dir = "/images/" + DateTime.Now.Year + "/" + DateTime.Now.Month + "/" + DateTime.Now.Day + "/"; Directory.CreateDirectory(Path.GetDirectoryName(context.Request.MapPath(dir))); string newfileName = Guid.NewGuid().ToString(); string fullDir = dir + newfileName + ext; using (FileStream stream = File.OpenWrite(context.Request.MapPath(fullDir))) { //将文件流写到指定的文件下 context.Request.InputStream.CopyTo(stream); MyImageServerEntities db = new MyImageServerEntities(); ImageInfo imageInfo = new ImageInfo(); imageInfo.ImageName = fullDir; imageInfo.ImageServerId = serverId; db.ImageInfo.Add(imageInfo); db.SaveChanges(); } } public bool IsReusable { get { return false; } } } }
这个处理程序会把接收的图片存储到服务器上,并且把途径插入数据库的表中,这里要引用一下EF实体层的ImageSystem.Model
,此时,就可以启动3个项目了,然后主web服务器上传图片,此时就根据随机算法存储图片。
后期,如公司继续增加图片文件服务器,你只需要在数据库中加一个ip地址就行了,然后再部署一台文件服务器,主web服务器无需重新发版,就可热使用。
以上便是利用C#和sqlserver数据库做的图片文件分布式存储方案设计模式!