• 断点续传(上传)C#版


    1. 客户每次上传前先获取一下当前文件已经被服务器接受了多少

    2. 上传时设定偏移量

    服务端代码如下:

    /// <summary>
    /// 断点续传,获取已上传文件大小
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    public long GetFileLength()
    {
        long result = 0;
        FileStream fStream = null;
        BinaryReader bReader = null;
        try
        {
            var fileName = "";
            IEnumerable<string> hasFileNameList = new List<string>();
            bool hasFileName = Request.Headers.TryGetValues("FileName", out hasFileNameList);
            if (hasFileName && hasFileNameList.Any())
            {
                fileName = hasFileNameList.First();
            } 
            //设置文件存放路径 
            string dir = HttpContext.Current.Server.MapPath(@"~UploadFiles");
            fileName= dir+"\"+fileName;
            if (File.Exists(fileName))
            {
                FileInfo file = new FileInfo(fileName);
                result = file.Length;
            } 
        }
        catch (Exception ex)
        {
            logger.Error(ex, ex.Message); 
        }
        return result;
    }
    
    
    /// <summary>
    /// 断点续传 服务端保存文件代码
    /// <param>Offset 偏移量</param>
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    public Bussiness<bool> UploadFile()
    {
        #region  Log Test
        FileStream fStreamLog = null;
        BinaryReader bReaderLog = null;
        try
        {
            fStreamLog = new FileStream(SavePath + "\" + $"{DateTime.Now:HHmmss}.File.tmp", FileMode.OpenOrCreate, FileAccess.ReadWrite);
            int upLoadLengthTest = (int)HttpContext.Current.Request.InputStream.Length;
            bReaderLog = new BinaryReader(HttpContext.Current.Request.InputStream);
            byte[] dataTest = new byte[upLoadLengthTest];
            bReaderLog.Read(dataTest, 0, upLoadLengthTest); //将上传文件流读到byte[]中
            fStreamLog.Write(dataTest, 0, upLoadLengthTest); //将byte[]写到文件中,
        }
        catch (Exception exr)
        {
            logger.Error(exr, exr.Message);
        }
        finally
        {
            if (fStreamLog != null)
            {
                //释放流
                fStreamLog.Close();
            }
            if (bReaderLog != null)
            {
                bReaderLog.Close();
            }
        }
        #endregion 
    
        var uploadFileName = HttpContext.Current.Request.Form["UploadFileName"];
        
        Bussiness<bool> result = new Bussiness<bool>();
        FileStream fStream = null;
        BinaryReader bReader = null;
        try
        {
            long offset = 0;  //客户端定义,从X开始往后写,该值由服务端GetFileLength方法提供
            IEnumerable<string> hasOffsetList = new List<string>();
            bool hasOffset = Request.Headers.TryGetValues("Offset", out hasOffsetList);
            if (hasOffset && hasOffsetList.Any())
            {
                long.TryParse(hasOffsetList.First(), out offset);
            }
            var offsetStr = HttpContext.Current.Request.Form["Offset"];
            if (!string.IsNullOrEmpty(offsetStr))
            {
                long.TryParse(offsetStr, out offset);
            }
            if (string.IsNullOrEmpty(uploadFileName))
            {
                result.BussinessCode = -1;
                result.BussinessMsg = "uploadFileName 上传文件名不能为空";
                result.BussinessData = false;
                return result;
            }
            //设置文件存放路径
            //string dir = HttpContext.Current.Server.MapPath(SavePath);
            string dir = SavePath;
            //如果不存在文件夹,就创建文件夹
            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);
            //////var fileName = dir + "\" + file.FileName; ///TODO
            var fileName = dir + "\" + (string.IsNullOrEmpty(uploadFileName) ? $"{DateTime.Now.ToString("HHmmss")}.txt" : uploadFileName);
            fStream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            //偏移指针
            fStream.Seek(offset, SeekOrigin.Begin);
        
            if (HttpContext.Current.Request.Files.Count > 0)
            {
                #region 文件形式
        
                HttpPostedFile file = HttpContext.Current.Request.Files["UploadFile"];
                int upLoadLength = Convert.ToInt32(file.InputStream.Length);
                //从客户端的请求中获取文件流
                bReader = new BinaryReader(file.InputStream);
                byte[] data = new byte[upLoadLength];
                bReader.Read(data, 0, upLoadLength); //将上传文件流读到byte[]中
                fStream.Write(data, 0, upLoadLength);//将byte[]写到文件中, 
        
                logger.Info($"上传文件 {uploadFileName} Offset => {offset} File.ContentLength => {upLoadLength} => {fileName}");
                #endregion
            }
            else
            {
                #region Base64
        
                //如果没传文件是以byte[]
                var byteFile = HttpContext.Current.Request.Form["UploadFile"];
                byte[] byteArray = Convert.FromBase64String(byteFile);
                int upLoadLength = Convert.ToInt32(byteArray.Length);
                fStream.Write(byteArray, 0, upLoadLength);//将byte[]写到文件中,  
                logger.Info($"上传文件 {uploadFileName} Offset => {offset} File.ContentLength => {upLoadLength} => {fileName}"); 
        
                #endregion
            }
            result.BussinessData = true;
            result.BussinessMsg = $"上传成功!";
            result.BussinessCode = 0;
        }
        catch (Exception ex)
        {
            logger.Error(ex, ex.Message);
            result.BussinessCode = -1;
            result.BussinessMsg = ex.Message;
            result.BussinessData = false;
        
            #region 出异常后,看看客户端上传的啥
        
            FileStream fstreamErr = null;
            BinaryReader readerErr = null;
            try
            {
                fstreamErr = new FileStream(SavePath + "\" + $"{uploadFileName}_{DateTime.Now:HHmmss}.err", FileMode.OpenOrCreate, FileAccess.ReadWrite);
                int upLoadLength = (int)HttpContext.Current.Request.InputStream.Length;
                readerErr = new BinaryReader(HttpContext.Current.Request.InputStream);
                byte[] data = new byte[upLoadLength];
                readerErr.Read(data, 0, upLoadLength); //将上传文件流读到byte[]中
                fstreamErr.Write(data, 0, upLoadLength); //将byte[]写到文件中,
            }
            catch (Exception exr)
            {
                logger.Error(exr, exr.Message);
            }
            finally
            {
                if (fstreamErr != null)
                {
                    //释放流
                    fstreamErr.Close();
                }
                if (readerErr != null)
                {
                    readerErr.Close();
                }
            }
        
            #endregion
        
            return result;
        }
        finally
        {
            if (fStream != null)
            {
                //释放流
                fStream.Close();
            }
            if (bReader != null)
            {
                bReader.Close();
            }
        }
        return result;
    }

    上传前,源和目标文件对比

    用 Postman 测试

    先获取已上传文件的大小,供下面设置偏移量使用

     

     

    上传后的源和目标文件对比

     


    java 客户端代码

    package com.vipsoft.demo.Controller;
    
    
    import org.springframework.core.io.FileSystemResource;
    import org.springframework.util.LinkedMultiValueMap;
    import org.springframework.util.MultiValueMap;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.File;
    import java.io.RandomAccessFile;
    import java.util.Base64;
    
    @RestController
    @RequestMapping("/demo")
    public class DemoController {
    
    
        @GetMapping(value = "/test")
        public String login() {
            return "这是demo下面的,用来写测试代码,测试通过后,移到正式项目中!";
        }
        
        @GetMapping(value = "/uploadFile")
        public String uploadFile(HttpServletRequest request) throws Exception {
    
            String url = "http://localhost:44999/api/Upload/UploadFile";
            RestTemplate restTemplate = new RestTemplate();
            String filePath = "D:\Projects\TEST.DAT";
            File file = new File(filePath);
            FileSystemResource resource = new FileSystemResource(file);
            MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
            param.add("Offset", "6");
            param.add("UploadFileName", "ABC.txt");
            param.add("UploadFile", resource);
    
            String rev = restTemplate.postForObject(url, param, String.class);
            System.out.println(rev);
            return rev;
        }
    
        @GetMapping(value = "/uploadByte")
        public String uploadByte(HttpServletRequest request) throws Exception {
            String url = "http://localhost:44999/api/Upload/UploadFile";
            RestTemplate restTemplate = new RestTemplate();
            String filePath = "D:\Projects\TEST.DAT";
            File file = new File(filePath);
            //每次上传 10KB
            int step = 10 * 1024;
            for (long i = 0; i <= file.length(); i = i + step) {
                //偏移量
                long offset = i;
                byte[] bytes = new byte[step];
                //如果超过了就取最后的。
                if (i + step > file.length()) {
                    bytes = new byte[(int) (file.length() - i)];
                }
                RandomAccessFile raf = new RandomAccessFile(file, "r");
                raf.seek(offset);
                int readSize = raf.read(bytes);
                MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
                param.add("Offset", i);
                param.add("UploadFileName", "ABC.txt");
                //转成 Base64
                param.add("UploadFile", Base64.getEncoder().encodeToString(bytes));
                String rev = restTemplate.postForObject(url, param, String.class);
                //如果接口返回值出错,break
                System.out.println(rev);
            }
            return "";
        }
    
    }

    C# 客户端伪代码

    /// <summary>
    /// 客户端上传伪代码
    /// </summary>
    /// <param name="fileName">待上传文件全路径</param>
    /// <param name="byteCount">上传时的流量控制,文件较大时,用于切割文件上传</param>
    /// <param name="msg">错误信息</param>
    /// <returns>成功或者失败</returns>
    public static bool UpLoadFile(string fileName, int byteCount, out string msg)
    {
        msg = "";
        bool result = true;
        long cruuent = GetServerFileLength(fileName); //客户端需要读取服务器已上传了多少内容,然后从偏移量开始往后读取发送
    
        FileStream fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
        BinaryReader bReader = new BinaryReader(fStream);
        long length = fStream.Length;
    
        fileName = fileName.Substring(fileName.LastIndexOf('\') + 1);
    
        #region 开始上传文件
        try
        {
            #region 续传处理
            byte[] data;
            if (cruuent > 0)
            {
                fStream.Seek(cruuent, SeekOrigin.Current);
            }
            #endregion
    
            #region 分割文件上传
            for (; cruuent <= length; cruuent = cruuent + byteCount)
            {
                try
                {
                    if (cruuent + byteCount > length)
                    {
                        data = new byte[Convert.ToInt64((length - cruuent))];
                        bReader.Read(data, 0, Convert.ToInt32((length - cruuent)));
                    }
                    else
                    {
                        data = new byte[byteCount];
                        bReader.Read(data, 0, byteCount);
                    }
    
                    Hashtable parms = new Hashtable(); 
                    parms.Add("Offset", cruuent.ToString()); 
                    var falg= PostData("",param);
                    if (falg == false)
                    {
                        break;
                    }
    
                }
                catch (Exception ex)
                {
                    msg = ex.ToString();
                    result = false;
                    break;
                }
                #endregion
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
        finally
        {
            bReader.Close();
            fStream.Close();
        }
        GC.Collect(); 
        #endregion 
        return result; 
    }
  • 相关阅读:
    谷歌地球服务器"失联"的替代方案
    Win32Api -- 回到Windows桌面
    WPF -- 应用启动慢问题
    Windows -- 多网卡上网设置
    .Net -- ConfigurationSection的简单使用
    WPF -- 使用RenderTargetBitmap将Canvas保存为图片
    WPF -- 使用当前进程打开自定义文件的一种方式
    WPF源码阅读 -- InkCanvas选中笔迹
    WPF源码阅读 -- InkCanvas选择模式
    WPF -- 使用Blend工具绘制Control样式
  • 原文地址:https://www.cnblogs.com/vipsoft/p/14419197.html
Copyright © 2020-2023  润新知