断点续传(上传)C#版

简介: 断点续传(上传)C#版

断点续传(上传)Java版

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; 
}

 

目录
相关文章
|
6月前
视频上传
视频上传
57 0
|
6月前
|
前端开发 NoSQL Redis
如何实现大文件上传:秒传、断点续传、分片上传
如何实现大文件上传:秒传、断点续传、分片上传
484 0
|
6月前
大文件上传如何断点续传
该文档描述了一个大文件上传流程,包括:1) 文件分片,2) 计算文件及分片的Hash值以生成唯一标识符,3) 上传分片并检查已上传状态以避免重复,4) 在上传中断时能恢复,5) 服务端合并分片成原始文件,6) 错误处理(如网络中断、服务器故障、上传失败等)并通知用户,最后7) 返回上传成功信息。
|
前端开发 JavaScript Dubbo
大文件上传:秒传、断点续传、分片上传
大文件上传:秒传、断点续传、分片上传
大文件上传:秒传、断点续传、分片上传
文件下载
文件下载
79 0
|
网络协议 Java
文件断点续传原理与实现
在网络状况不好的情况下,对于文件的传输,我们希望能够支持可以每次传部分数据。首先从文件传输协议FTP和TFTP开始分析, FTP是基于TCP的,一般情况下建立两个连接,一个负责指令,一个负责数据;而TFTP是基于UDP的,由于UDP传输是不可靠的,虽然传输速度很快,但对于普通的文件像PDF这种,少了一个字节都不行。
2696 0
|
缓存 C# 图形学
C#多线程下载、断点续传的实现
做Unity热更功能的时候,发现单线程下载大尺寸资源文件的效率太低,专门去研究了下多线程下载,这里记录下相关知识点。
1057 0
|
Android开发 数据格式 XML
okhttp3下载文件检测进度与断点续传
之前有用过retrofit来做下载的功能,虽然retrofit基于okhttp,但是这还是有点不同。 我是在做更新功能的时候用到这个,具体的操作可能不会说太多,因为网上能找到很多基本的操作,我就说下一些流程和BUG,不管是okhttp还是retrofit都适用。
2365 0