博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
我心中的核心组件(可插拔的AOP)~分布式文件上传组件~基于FastDFS
阅读量:7202 次
发布时间:2019-06-29

本文共 13769 字,大约阅读时间需要 45 分钟。

一些概念

在大叔框架里总觉得缺点什么,在最近的项目开发中,终于知道缺什么了,分布式文件存储组件,就是缺它,呵呵,对于分布式文件存储来说,业界比较公认的是FastDFS组件,它自己本身就是集群机制,有自己的路由选择和文件存储两个部分,我们通过FastDFS的客户端进行上传后,它会返回一个在FastDFS上存储的路径,这当然是IO路径,我们只要在服务器上开个Http服务器,就可以以Http的方法访问你的文件了。

我的组件实现方式

前端上传控件(表单方式,swf方式,js方法均可)将文件流传给我们的FastDFS客户端,通过客户端与服务端建立Socket连接,将数据包发给FastDFS服务端并等待返回,上传成功后返回路径,我们可以对路径进行HTTP的处理,并存入数据库

fastDFS配合nginx服务器自动生成指定尺寸的图像

原图像地址: http://www.fastdfs.com/demo/pictruename.jpg

指定尺寸的图像地址:http://www.fastdfs.com/demo/pictruename_100x100.jpg

技术实现

1 一个接口,定义三种上传规格,普通文件,图像文件和视频文件(一般需要对它进行截力)

public interface IFileUploader    {        ///         /// 上传视频文件        ///         ///         /// 
VideoUploadResult UploadVideo(VideoUploadParameter param); /// /// 上传普通文件 /// /// ///
FileUploadResult UploadFile(FileUploadParameter param); /// /// 上传图片 /// /// ///
///
Update:cyr(Ben) 20150317
ImageUploadResult UploadImage(ImageUploadParameter param); }

2 一批方法参数,包括了文件,图像和视频等

///     /// 文件上传参数基类    ///     public abstract class UploadParameterBase    {        public UploadParameterBase()        {            MaxSize = 1024 * 1024 * 8;        }        ///         /// 前一次上传时生成的服务器端文件名,如果需要断点续传,需传入此文件名        ///         public string ServiceFileName { get; set; }        ///         /// 文件流        ///         public Stream Stream { get; set; }        ///         /// 文件名        ///         public string FileName { get; set; }        ///         /// 文件大小限制(单位bit 默认1M)        ///         public int MaxSize        {            get;            protected set;        }        ///         /// 上传文件类型限制        ///         public string[] FilenameExtension        {            get;            set;        }    }
///     /// 图片上传参数对象    ///     public class ImageUploadParameter : UploadParameterBase    {        ///         /// 构造方法        ///         ///         ///         /// 默认支持常用图片格式        ///         public ImageUploadParameter(Stream stream, string fileName, string[] filenameExtension = null, int maxSize = 3)        {            base.Stream = stream;            base.FileName = fileName;            base.MaxSize = maxSize;            base.FilenameExtension = filenameExtension ?? new string[] { ".jpeg", ".jpg", ".gif", ".png" }; ;        }        ///         /// 构造方法        ///         ///         ///         /// 单位为M        public ImageUploadParameter(Stream stream, string fileName, int maxSize)            : this(stream, fileName, null, maxSize) { }    }

3 一批返回类型,包括对文件,图像和视频等方法的返回数据的定义

  ///     /// 上传文件返回对象基类    ///     public abstract class UploadResultBase    {        ///         /// 返回文件地址        ///         public string FilePath { get; set; }        ///         /// 错误消息列表        ///         public string ErrorMessage { get; set; }        ///         /// 是否上传成功        ///         public bool IsValid { get { return string.IsNullOrWhiteSpace(ErrorMessage); } }    }
///     /// 视频上传返回对象    ///     public class VideoUploadResult : UploadResultBase    {        ///         /// 上传的视频截图地址        ///         public List
ScreenshotPaths { get; set; } ///
/// 上传状态 /// public UploadStatus UploadStatus { get; set; } public VideoUploadResult() { ScreenshotPaths = new List
(); } ///
/// 把VideoPath和ScreenshotPaths拼起来 以竖线(|)隔开 /// ///
public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append(FilePath); foreach (var item in ScreenshotPaths) { sb.Append("|" + item); } return sb.ToString(); } }

4 一个使用FastDFS实现的文件上传实现类

///     /// 使用fastDFS完成文件上传    ///     internal class FastDFSUploader : IFileUploader    {        ///         /// 目录名,需要提前在fastDFS上建立        ///         public string DFSGroupName { get { return "tsingda"; } }        ///         /// FastDFS结点        ///         public StorageNode Node { get; private set; }        ///         /// 服务器地址        ///         public string Host { get; private set; }        ///         /// 失败次数        ///         protected int FaildCount { get; set; }        public int MaxFaildCount { get; set; }        public FastDFSUploader()        {            InitStorageNode();            MaxFaildCount = 3;        }        #region Private Methods        private void InitStorageNode()        {            Node = FastDFSClient.GetStorageNode(DFSGroupName);            Host = Node.EndPoint.Address.ToString();        }        private List
CreateImagePath(string fileName) { List
pathList = new List
(); string snapshotPath = ""; //视频截图 List
localList = new VideoSnapshoter().GetVideoSnapshots(fileName, out snapshotPath); foreach (var item in localList) { string aImage = SmallFileUpload(item); pathList.Add(aImage); } //清除本地多余的图片,有的视频截取的图片多,有的视频截取的图片少 string[] strArr = Directory.GetFiles(snapshotPath); try { foreach (var strpath in strArr) { File.Delete(strpath); } Directory.Delete(snapshotPath); } catch (Exception ex) { Logger.Core.LoggerFactory.Instance.Logger_Info("删除图片截图异常" + ex.Message); } return pathList; } private string SmallFileUpload(string filePath) { if (string.IsNullOrEmpty(filePath)) throw new ArgumentNullException("filePath 参数不能为空"); if (!File.Exists(filePath)) throw new Exception("上传的文件不存在"); byte[] content; using (FileStream streamUpload = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { using (BinaryReader reader = new BinaryReader(streamUpload)) { content = reader.ReadBytes((int)streamUpload.Length); } } string shortName = FastDFSClient.UploadFile(Node, content, "png"); return GetFormatUrl(shortName); } ///
/// 文件分块上传,适合大文件 /// ///
///
private string MultipartUpload(UploadParameterBase param) { Stream stream = param.Stream; if (stream == null) throw new ArgumentNullException("stream参数不能为空"); int size = 1024 * 1024; byte[] content = new byte[size]; Stream streamUpload = stream; // 第一个数据包上传或获取已上传的位置 string ext = param.FileName.Substring(param.FileName.LastIndexOf('.') + 1); streamUpload.Read(content, 0, size); string shortName = FastDFSClient.UploadAppenderFile(Node, content, ext); BeginUploadPart(stream, shortName); return CompleteUpload(stream, shortName); } ///
/// 断点续传 /// ///
///
private void ContinueUploadPart(Stream stream, string serverShortName) { var serviceFile = FastDFSClient.GetFileInfo(Node, serverShortName); stream.Seek(serviceFile.FileSize, SeekOrigin.Begin); BeginUploadPart(stream, serverShortName); } ///
/// 从指定位置开始上传文件 /// ///
///
///
private void BeginUploadPart(Stream stream, string serverShortName) { try { int size = 1024 * 1024; byte[] content = new byte[size]; while (stream.Position < stream.Length) { stream.Read(content, 0, size); var result = FastDFSClient.AppendFile(DFSGroupName, serverShortName, content); if (result.Length == 0) { FaildCount = 0; continue; } } } catch (Exception ex) { Logger.Core.LoggerFactory.Instance.Logger_Info("上传文件中断!" + ex.Message); if (NetCheck()) { //重试 if (FaildCount < MaxFaildCount) { FaildCount++; InitStorageNode(); ContinueUploadPart(stream, serverShortName); } else { Logger.Core.LoggerFactory.Instance.Logger_Info("已达到失败重试次数仍没有上传成功"); ; throw ex; } } else { Logger.Core.LoggerFactory.Instance.Logger_Info("当前网络不可用"); throw ex; } } } ///
/// 网络可用为True,否则为False /// ///
private bool NetCheck() { return NetworkInterface.GetIsNetworkAvailable(); } ///
/// 拼接Url /// ///
///
private string GetFormatUrl(string shortName) { return string.Format("http://{0}/{1}/{2}", Host, DFSGroupName, shortName); } private string CompleteUpload(Stream stream, string shortName) { stream.Close(); return GetFormatUrl(shortName); } private string GetShortNameFromUrl(string url) { if (string.IsNullOrEmpty(url)) return string.Empty; Uri uri = new Uri(url); string urlFirstPart = string.Format("http://{0}/{1}/", Host, DFSGroupName); if (!url.StartsWith(urlFirstPart)) return string.Empty; return url.Substring(urlFirstPart.Length); } #endregion #region IFileUploader 成员 ///
/// 上传视频 /// ///
///
public VideoUploadResult UploadVideo(VideoUploadParameter param) { VideoUploadResult result = new VideoUploadResult(); string fileName = MultipartUpload(param); if (param.IsScreenshot) { result.ScreenshotPaths = CreateImagePath(fileName); } result.FilePath = fileName; return result; } ///
/// 上传普通文件 /// ///
///
public FileUploadResult UploadFile(FileUploadParameter param) { var result = new FileUploadResult(); try { string fileName = MultipartUpload(param); result.FilePath = fileName; } catch (Exception ex) { result.ErrorMessage = ex.Message; } return result; } ///
/// 上传图片 /// ///
///
///
public ImageUploadResult UploadImage(ImageUploadParameter param) { byte[] content; string shortName = ""; string ext = System.IO.Path.GetExtension(param.FileName).ToLower(); if (param.FilenameExtension != null && param.FilenameExtension.Contains(ext)) { if (param.Stream.Length > param.MaxSize) { return new ImageUploadResult { ErrorMessage = "图片大小超过指定大小" + param.MaxSize / 1048576 + "M,请重新选择", FilePath = shortName }; } else { using (BinaryReader reader = new BinaryReader(param.Stream)) { content = reader.ReadBytes((int)param.Stream.Length); } shortName = FastDFSClient.UploadFile(Node, content, ext.Contains('.') ? ext.Substring(1) : ext); } } else { return new ImageUploadResult { ErrorMessage = "文件类型不匹配", FilePath = shortName }; } return new ImageUploadResult { FilePath = CompleteUpload(param.Stream, shortName), }; } #endregion }

5 一个文件上传的生产者,经典的单例模式的体现

///     /// 文件上传生产者    ///     public class FileUploaderFactory    {        ///         /// 上传实例        ///         public readonly static IFileUploader Instance;        private static object lockObj = new object();        static FileUploaderFactory()        {            if (Instance == null)            {                lock (lockObj)                {                    Instance = new FastDFSUploader();                }            }        }    }

6 前台的文件上传控件,你可以随便选择了,它与前台是解耦的,没有什么关系,用哪种方法实现都可以,呵呵

    [HttpPost]        public ActionResult Index(FormCollection form)        {            var file = Request.Files[0];            var result = Project.FileUpload.FileUploaderFactory.Instance.UploadFile(new Project.FileUpload.Parameters.FileUploadParameter            {                FileName = file.FileName,                Stream = file.InputStream,            });            ViewBag.Path = result.FilePath;            return View();        }

最后我们看一下我的Project.FileUpload的完整结构

它隶属于大叔的Project.Frameworks集合,我们在这里,对Project.FileUpload说一声:Hello FileUpload,We wait for you for a long time...

 

转载地址:http://cewum.baihongyu.com/

你可能感兴趣的文章
GrideView 网格控件
查看>>
系统管理员-Linux基础学习-第一部分内容。
查看>>
Django 入门学习(3)
查看>>
Powershell + Nagios 监控 VEEAM 备份状态
查看>>
布雷(扫雷游戏)
查看>>
收集整理了一些免费的区块链、以太坊技术开发相关的文件,有需要的拿去
查看>>
SKYPE原理分析
查看>>
Add Digits(leetcode258)
查看>>
6.3-全栈Java笔记:异常处理方法(上)
查看>>
Spark 性能相关参数配置详解-Storage篇
查看>>
十四,泛型(Generics)
查看>>
C3P0详细配置及其说明
查看>>
RedHat linux系统加固
查看>>
Linux下 RabbitMQ的安装与配置
查看>>
shell编程——$数字的含义
查看>>
Tomcat 安全配置与性能优化
查看>>
PHP教程:我们什么时候应该使用异常?
查看>>
我的友情链接
查看>>
搭建 vsftpd FTP服务器
查看>>
安全技术积累-搜索目标漏洞20163月
查看>>