zoukankan      html  css  js  c++  java
  • Asp.Net Core 2.0 WebUploader FastDfs 文件上传 分段上传

    功能点:

    1. 使用.net core 2.0 实现文件上传

    2. 使用webuploader实现单文件,多文件上传

    3. 使用webuploader实现大文件的分段上传。

    4. 使用webuploader与FastDfs实现文件上传。

    5. 通过依赖注入可以很方便地实现本地文件存储切换为FastDfs

    主要功能:

    直接上代码:

    1. 使用.net core 2.0 实现文件上传

            public ActionResult Upload(IFormCollection files)
            {
                var result = new UploadResult();
    
                foreach (var item in files.Files)
                {
                    #region 把文件流转化为字节流
                    byte[] buffer = new byte[item.Length];
    
                    Stream fs = item.OpenReadStream();
    
                    fs.Read(buffer, 0, buffer.Length);
                    #endregion
    
                    UploadConfig config = new UploadConfig
                    {
                        Buffer = buffer,
                        FileName = item.FileName,
                        Chunked = files.Keys.Contains("chunk"),
                        PreviosName = files["previosName"]
                    };
    
                    result = _provider.Upload(config);
                }
                return Json(result);
            }

    这里要注意参数,使用的是IFormCollection,没有使用IFormFile,使用后者的话,不能接收前台发送的自定义参数。虽然我用了fforeach循环,但实现上它的files count只有一个,不管前台发送的是一个文件,还是多个文件,这点还要研究一下,是不是有配置。

            private readonly IFileSystemProvider _provider;
    
            public HomeController(IFileSystemProvider provider)
            {
                _provider = provider;
    
            }
    IFileSystemProvider 为定义的接口,分别有本地文件存储,和分布式文件存储两种实现方式,后面会奉上代码。

    2. 使用webuploader实现单文件,多文件上传
    这里主要上前台代码:
            // 实例化
            uploader = WebUploader.create({
                pick: {
                    id: '#filePicker',
                    label: '点击选择文件'
                },
                dnd: '#dndArea',
                paste: '#uploader',
                swf:'lib/webuploader/Uploader.swf',
                chunked: true, //分片处理大文件
                chunkSize: 1 * 1024 * 1024,
                server: home/upload,
                disableGlobalDnd: true,
                threads: 1, //上传并发数
                fileNumLimit: 300,
                compress: false, //图片在上传前不进行压缩
                fileSizeLimit: 1024 * 1024 * 1024,    // 1024 M
                fileSingleSizeLimit: 1024 * 1024 * 1024    // 1024 M
            });

    初始化webuploader这里要注意threads我想的是1,如果分段上传,几个分段同时发送的话,后台如果写入是个问题,特别是使用fastdfs, .net版本的sdk还不支持指定位置写入的功能。

    注意到一个事件:它是实现分段上传重中之重

            uploader.on('uploadAccept', function (file, response) {
                if (file.chunk + 1 !== file.chunks) {
                    $.extend(uploader.options.formData, { previosName: response.filePath });
                } else {
                    $.extend(uploader.options.formData, { previosName: "" });
                }
                return true;
            });

    这里着重解释一下。看到网上关于文件分段上传,都是在服务器端用临时文件记录着文件上传的地址,当有分段信息到达时,会查询临时文件,取出文件地址,然后再往这个地址追加文件流。

    这样增加了服务器端的复杂度,而且如果是分布式部署的话,会存在找到临时文件的问题,进而会考虑到数据库和分布式缓存的解决方案,更加复杂了。

    这里简单说明一下解决思路:

    1) 后台:第一段上传成功后,返回文件地址。如代码的 return Json(result);

    2) 前台:使用uploadAccept事件接收文件信息和后台返回地址。

    3)前台:在uploadAccept事件中:添加往后台发送的参数previosName:第一段返回的文件地址。

       这里要注意如果是多个文件上传的话,第二个文件的第一个分段不能带有第一个文件的上传地址,如上代码所示

    4) 后台:如上面的代码所示,接收前台发送的文件地址,做文件的追加,即可。

    3. 使用webuploader实现大文件的分段上传。

    下面是服务端的代码:

            public UploadResult Upload(UploadConfig config)
            {
                //相对路径
                string path = GetFileName(config);
    
                //根据路径创建一个新的文件,并在指定位置写入字节,如果文件已存在,就追加
                using (FileStream fs = File.OpenWrite(Path.Combine(_option.RootFilePath, path)))
                {
                    if (fs.CanWrite)
                    {
                        fs.Seek(fs.Length, SeekOrigin.Current);
                        fs.Write(config.Buffer, 0, config.Buffer.Length);
                        fs.Flush();
                        fs.Close();
                    }
                }
    
                return new UploadResult { FilePath = path, OriginalName = config.FileName };
            }
    
            private string GetFileName(UploadConfig config)
            {
                string path;
    
                if (string.IsNullOrEmpty(config.PreviosName))
                {
                    path = Path.Combine(subDirectory, $"{GuidTo16String()}.{config.FileName}"); //返回的相对路径和文件名
    
                    CheckSavePath();
                }
                else
                {
                    path = config.PreviosName;
                    if (string.IsNullOrEmpty(config.PreviosName))
                    {
                        throw new ArgumentNullException("PreviosName 参数不能为空");
                    }
                }
    
                return path;
            }
    
            private string CheckSavePath()
            {
                var path = Path.Combine(_option.RootFilePath, subDirectory);
    
                // 如何路径不存在,就创建文件路径
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
    
                return path;
            }

    这里关注一下GetFileName方法:代码很简单,就不做说明了。

    4. 使用webuploader与FastDfs实现文件上传

    前端代码和服务端的action的代码是一样的。主要是写文件的方式不一样,主要代码如下:

            public UploadResult Upload(UploadConfig config)
            {
                if (string.IsNullOrEmpty(config.PreviosName))
                {
                    var fileName = string.Empty;
                    var storageNode = _client.GetStorageNode(config.GroupName);
    
                    if (!config.Chunked)
                    {
                        fileName = _client.UploadFile(storageNode, config.Buffer, Path.GetExtension(config.FileName));
                    }
                    else
                    {
                        //分段上传需要调用这个方法
                        fileName = _client.UploadAppenderFile(storageNode, config.Buffer, Path.GetExtension(config.FileName));
                    }
    
                    return new UploadResult { FilePath = fileName, OriginalName = config.FileName };
                }
                else
                {
                    //分段上传:需要提供上传GroupName, 文件上传地址PreviosName,文件上传内容filebody
                    //续传 地址config.PreviosName
                    _client.AppendFile(config.GroupName, config.PreviosName, config.Buffer);
    
                    return new UploadResult { FilePath = config.PreviosName, OriginalName = config.FileName };
                }
            }

    这里注意分段上传的文件,第一段上传使用的方法是UploadAppenderFile,后续使用的是AppendFile。

    连接FastDfs的代码如下:

        public class FastDfsClientProvider : IFastDfsClientProvider
        {
            private readonly FastDfsOption _options;
    
            public FastDfsClientProvider(FastDfsOption options)
            {
                _options = options;
            }
    
            public FastDfsClient GetClient()
            {
                return new FastDfsClient(new List<IPEndPoint>
                {
                    new IPEndPoint(IPAddress.Parse(_options.IpAddress), _options.Port)
                });
            }
        }

    5. 通过依赖注入可以很方便地实现本地文件存储切换为FastDfs

    代码如下:

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddMvc();
    
                
                services.AddTransient<IFileSystemProvider>(m=> {
                    IHostingEnvironment env = m.GetService<IHostingEnvironment>();
    
                    LocalFsOption lOption = new LocalFsOption
                    {
                        RootFilePath = Path.Combine(env.ContentRootPath, "Files")
                    };
                    return new LocalFsProvider(lOption);
                });
            }

    如果想换实现的话 改一个return new LocalFsProvider(lOption),配置一下参数就行了。

    部分代码参考博友,找不到出处了,请凉解。

  • 相关阅读:
    语文成语的积累
    关于翻译
    关于文常
    语文小说
    语文 散文阅读
    矩佬的博客
    字词
    语文病句
    地理
    寒假某cg的数学卷子....
  • 原文地址:https://www.cnblogs.com/hankuikui/p/9182073.html
Copyright © 2011-2022 走看看