我心中的核心组件(可插拔的AOP)~分布式文件上传组件~基于FastDFS

回到目录

一些概念

在大叔框架里总觉得缺点什么,在最近的项目开发中,终于知道缺什么了,分布式文件存储组件,就是缺它,呵呵,对于分布式文件存储来说,业界比较公认的是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
{
/// <summary>
/// 上传视频文件
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
VideoUploadResult UploadVideo(VideoUploadParameter param);
/// <summary>
/// 上传普通文件
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
FileUploadResult UploadFile(FileUploadParameter param);
/// <summary>
/// 上传图片
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
/// <remarks>Update:cyr(Ben) 20150317</remarks>
ImageUploadResult UploadImage(ImageUploadParameter param);
}

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

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

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

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

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

    /// <summary>
/// 使用fastDFS完成文件上传
/// </summary>
internal class FastDFSUploader : IFileUploader
{
/// <summary>
/// 目录名,需要提前在fastDFS上建立
/// </summary>
public string DFSGroupName { get { return "tsingda"; } }
/// <summary>
/// FastDFS结点
/// </summary>
public StorageNode Node { get; private set; }
/// <summary>
/// 服务器地址
/// </summary>
public string Host { get; private set; }
/// <summary>
/// 失败次数
/// </summary>
protected int FaildCount { get; set; } public int MaxFaildCount { get; set; } public FastDFSUploader()
{
InitStorageNode();
MaxFaildCount = ;
} #region Private Methods
private void InitStorageNode()
{
Node = FastDFSClient.GetStorageNode(DFSGroupName);
Host = Node.EndPoint.Address.ToString();
} private List<string> CreateImagePath(string fileName)
{
List<string> pathList = new List<string>();
string snapshotPath = "";
//视频截图
List<string> 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);
}
/// <summary>
/// 文件分块上传,适合大文件
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
private string MultipartUpload(UploadParameterBase param)
{
Stream stream = param.Stream;
if (stream == null)
throw new ArgumentNullException("stream参数不能为空");
int size = * ;
byte[] content = new byte[size];
Stream streamUpload = stream;
// 第一个数据包上传或获取已上传的位置
string ext = param.FileName.Substring(param.FileName.LastIndexOf('.') + );
streamUpload.Read(content, , size);
string shortName = FastDFSClient.UploadAppenderFile(Node, content, ext); BeginUploadPart(stream, shortName); return CompleteUpload(stream, shortName);
}
/// <summary>
/// 断点续传
/// </summary>
/// <param name="stream"></param>
/// <param name="serverShortName"></param>
private void ContinueUploadPart(Stream stream, string serverShortName)
{
var serviceFile = FastDFSClient.GetFileInfo(Node, serverShortName);
stream.Seek(serviceFile.FileSize, SeekOrigin.Begin);
BeginUploadPart(stream, serverShortName);
}
/// <summary>
/// 从指定位置开始上传文件
/// </summary>
/// <param name="stream"></param>
/// <param name="beginOffset"></param>
/// <param name="serverShortName"></param>
private void BeginUploadPart(Stream stream, string serverShortName)
{
try
{
int size = * ;
byte[] content = new byte[size]; while (stream.Position < stream.Length)
{
stream.Read(content, , size); var result = FastDFSClient.AppendFile(DFSGroupName, serverShortName, content);
if (result.Length == )
{
FaildCount = ;
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;
}
}
}
/// <summary>
/// 网络可用为True,否则为False
/// </summary>
/// <returns></returns>
private bool NetCheck()
{
return NetworkInterface.GetIsNetworkAvailable();
}
/// <summary>
/// 拼接Url
/// </summary>
/// <param name="shortName"></param>
/// <returns></returns>
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 成员 /// <summary>
/// 上传视频
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
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;
} /// <summary>
/// 上传普通文件
/// </summary>
/// <param name="param"></param>
/// <returns></returns>
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;
} /// <summary>
/// 上传图片
/// </summary>
/// <param name="param"></param>
/// <param name="message"></param>
/// <returns></returns>
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 / + "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() : ext);
}
}
else
{
return new ImageUploadResult
{
ErrorMessage = "文件类型不匹配",
FilePath = shortName
}; }
return new ImageUploadResult
{
FilePath = CompleteUpload(param.Stream, shortName),
};
}
#endregion
}

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

 /// <summary>
/// 文件上传生产者
/// </summary>
public class FileUploaderFactory
{
/// <summary>
/// 上传实例
/// </summary>
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[];
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的完整结构

aaarticlea/png;base64,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" alt="" />

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

回到目录

上一篇:数据结构 - 归并排序(merging sort)


下一篇:用css3实现鼠标移进去当前亮其他变灰