断点续传(上传)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;
}
断点续传(上传)C#版的更多相关文章
- wepy开发小程序 大坑....本地调试ok,小程序上传体验版 组件出现问题
如果你碰到的上述问题(本地调试ok,小程序上传体验版 各种莫名其妙的问题-卡死-组件属性失效-$apply()不起作用) 您需要关闭 微信开发者工具中: 1.微信开发者工具-->项目--> ...
- .net 实现上传文件分割,断点续传上传文件
一 介绍 断点续传搜索大部分都是下载的断点续传,涉及到HTTP协议1.1的Range和Content-Range头. 来个简单的介绍 所谓断点续传,也就是要从文件已经下载的地方开始继续下载.在以前版本 ...
- java实现多线程断点续传,上传下载
采用apache 的 commons-net-ftp-ftpclient import java.io.File; import java.io.FileOutputStream; import ja ...
- java http大文件断点续传上传
因为需要研究下断点上传的问题.找了很久终于找到一个比较好的项目. 效果: 上传中,显示进度,时间,百分比. 点击[Pause]暂停,点击[Resume]继续. 2,代码分析 项目进行了封装使用最简单的 ...
- h5图片上传简易版(FileReader+FormData+ajax)
一.选择图片(input的file类型) <input type="file" id="inputImg"> 1. input的file类型会渲染为 ...
- java文件断点续传上传下载解决方案
这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数 下面直接贴代码吧,一些难懂的我大部分都加上注释了: 上传文件实体类: 看得 ...
- 关于:基于http协议大文件断点续传上传至web服务器
关键部分 前端用file.slice()分块 前端用FileReader获取每一分块的md5值 后端用MultipartFile接受分块文件 后端用FileOutputStream拼装分块文件 话不多 ...
- wangeditor 支持上传视频版
1.关于使用哪个富文本编辑器. 简单的要求,不要求发布出来的文章排版要求很高. 可用wangediter.(简单,体积小,不可修改上传图片的尺寸大小) 转载 来源: https://blog.csd ...
- h5语音录制及上传(Java版语音聊天系统)
Since Chrome version 47, Voice Recording works only on HTTPS sites 目前基于webikit(谷歌之类的webikit)和Gecko(F ...
- django文件批量上传-简写版
模板中创建表单 <form method='post' enctype='multipart/form-data' action='/upload/'> <input type='f ...
随机推荐
- 《最新出炉》系列初窥篇-Python+Playwright自动化测试-28-处理日历时间控件-上篇
1.简介 我们在实际工作中,有可能遇到有些web产品,网页上有一些时间选择,然后支持按照不同时间段范围去筛选数据,例如:我们预定火车票或者预定酒店,需要选择发车日期或者酒店的入住与退房时间.宏哥早在之 ...
- Linux 本地AMH 服务器管理面板实现远程访问方法
AMH 是一款基于 Linux 系统的服务器管理面板,它提供了一系列的功能,包括网站管理.FTP 管理.数据库管理.DNS 管理.SSL 证书管理等.使用 AMH 云主机面板可以方便地管理服务器,提高 ...
- super学习
2022-10-02 16:27:38 super super代表的是"当前对象(this)"的父类型特征 概念 1.super是一个关键字,全部小写. 2.super和this对 ...
- GitHub Desktop安装与使用教程
一.安装 1.下载 下载地址 2.安装 下载之后GitHub Desktop是没有安装步骤的,而是开始安装之后,稍等片刻就安装成功了. 然后登陆个人GitHub账号就可以进行一下操作了. 二.新建仓库 ...
- 2分钟,快速认识什么是SQL
结构化查询语言,简称SQL,它是与关系数据库管理系统通信的黄金标准语言.今天就来一起快速认识一下什么是SQL,您可以通过以下的文字内容学习,也可以通过文末的视频学习,希望本文对您有所帮助. 您可能听说 ...
- [NOI online2022提高B] 讨论
题目描述 有 \(n\) 个人正在打模拟赛,模拟赛有 \(n\) 道题目. 有两人都会的题目并且没有人会的题目包含另一个人时,两者之间才会讨论. (定义第 ii 个人会的题目的集合为 \(S_i\) ...
- finally中的代码一定会执行吗?
通常在面试中,只要是疑问句一般答案都是"否定"的,因为如果是"确定"和"正常"的,那面试官就没有必要再问了嘛,而今天这道题的答案也是符合这个 ...
- 36. 干货系列从零用Rust编写负载均衡及代理,内网穿透中内网代理的实现
wmproxy wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器,四层TCP/UDP转发,七层负载均衡,内网穿透,后续将实现websocket代 ...
- DRF过滤器
https://www.django-rest-framework.org/api-guide/filtering 一般情况下,我们可以重写DRF视图类的get_queryset()方法来实现查询结果 ...
- bash shell笔记整理——file命令
file命令的作用 查看一个给定参数的文件类型 file命令语法 file [FILE...] file命令还有选项,但是基本用的不太多,这个命令也基本用得不是很多. 示例 [root@nginx-p ...