方便学习 养成好习惯 勿喷
OSS的大文件的断点续存
oss的断点续传代码参考,记得先添加oss的相关dll文件 官网可以直接找到的
class AliOSS
{
private Properties.Settings setting = Properties.Settings.Default;
static OssClient client=new OssClient(endpoint, accessKeyId, accessKeySecret);
///endpoint 就是所在区域的阿里云服务器地址 如:"http://oss-cn-shanghai.aliyuncs.com";
accessKeyId, accessKeySecret 就是账号和密码啦
static AutoResetEvent _event = new AutoResetEvent(false);
static HashAlgorithm hashAlgorithm = new MD5CryptoServiceProvider();
string objectstring = "";
private Form1 form;
public AliOSS(Form1 form,string filepath,OssClient ossclient)
{
this.form = form;
client = ossclient;
}
public AliOSS()
{
// client = new OssClient();
}
//断点上传
public void OSSUpload(string bucketname ,string localFilename, string key,string checkpointdir)
{
try
{
///backetname 就是需要存储的仓库地址
///loacalfilename 就是你想要上传的本地文件的地址
///key 就是定义上传文件在aliyunoss 存储的名字
///checkpoint 就是那个断点文件的存储位置
///断点续传
// using (var fs = File.Open(localFilename, FileMode.Open))
// {
form.putObjectRequest = new UploadObjectRequest(bucketname, key, localFilename)
{
PartSize = 2 * 1024 * 1024,
ParallelThreadCount = 1,
CheckpointDir = checkpointdir,
// checkpointDir保存断点续传的中间状态,用于失败后继续上传。如果checkpointDir为null,断点续传功能不会生效,每次失败后都会重新上传。
checkpoint";//完全上传成功会清除 ----->填写上传的文件地址(无需文件名)
StreamTransferProgress = streamProgressCallback,
};
client.ResumableUploadObject(form.putObjectRequest);
// }
// 带进度条的上传
//using (var fs = File.Open(localFilename, FileMode.Open))
//{
// var putObjectRequest = new PutObjectRequest(bucketName, key, fs);
// putObjectRequest.StreamTransferProgress += streamProgressCallback;
// client.PutObject(putObjectRequest);
//}
}
catch (OssException ex)
{
// Console.WriteLine("Failed with error code: {0}; Error info: {1}. \nRequestID:{2}\tHostID:{3}",
// ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
MessageBox.Show("没有网了,请检查网络,或者点击继续上传");
}
catch (Exception ex)
{
//Console.WriteLine("Failed with error info: {0}", ex.Message);
MessageBox.Show("没有网了,请检查网络,或者点击继续上传");
}
}
///过程回调
private void streamProgressCallback(object sender, StreamTransferProgressArgs args)
{
form.progressBar1.Invoke(new Action(() => {
form.progressBar1.Minimum = 0;
form.progressBar1.Maximum =(int)args.TotalBytes;
form.progressBar1.Value = (int)args.TransferredBytes;
}));
if (args.TotalBytes == args.TransferredBytes)///传输结束的标志,里面的自己改
{
Form1.checkpointdir = "";
form.uploadflog = true;
}
// Console.WriteLine("ProgressCallback - TotalBytes:{0}, TransferredBytes:{1}, IncrementTransferred:{2}",
// args.TotalBytes, args.TransferredBytes, args.IncrementTransferred);
}
上面的代码是自己做测试的时候用的,写的有点乱。
注释掉的带进度条的是 oss的异步简单上传,也可以看看。
重点是那个checkpointdir 真的是坑死人啊 阿里的官方文档感觉对C#不重视,断点续传里面参数都懒得好好解释,也有可能是我没注意到。反正这个参数花了我半天才弄明白了。。。。。。
阿里的官方是这样写的:::
文件上传时,会在Checkpoint文件中记录当前上传的进度信息,如果上传过程中某一分片上传失败,再次上传时会从Checkpoint记录处继续上传,从而达到断点续传的效果。上传完成后,Checkpoint文件会被删除。
我在测试的过程中。利用断开网络连接的方式来查看这个文件的存储,我都放在上传文件的目录下,当然你也可以建立临时文件夹去存放。要是在数据传输的过程中断网,大概一分钟左右会报错,然后当你再次开始的时候并没有将原来的文件继续上传而是全部重新上传。这个地方我就一直很不明白。
然后就移步了分片传输了。
我比较推荐使用分片传输。因为只要uploacalID 和key相同的话就代表一次传输,就算断网在重新连接也会续传。他的传输过程的参数都比较好把握。不像断点续传那样把希望都放在自动生成的文件夹里。根本摸不清他是怎么想的 怎么做的
下面贴一下分片上传的代码(可以支持断点续传的功能)
using Aliyun.OSS;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
class MultipartUpload
{
static string accessKeyId = "阿里云账号";
static string accessKeySecret = "阿里云密码";
static string endpoint = "http://oss-cn-shanghai.aliyuncs.com";
static string bucketName = "文件需要上传到oss的仓库名";
static OssClient client = new OssClient(endpoint, accessKeyId, accessKeySecret);
static long partSize = 2 * 1024 * 1024;
private Form1 form;
///为了断点续传的备份数据
public int startcountback = 0;
public string uploadidback = "";
public List<PartETag> partETagsback = new List<PartETag>();
public MultipartUpload(Form1 form)
{
this.form = form;
}
/// <summary>
/// 分片上传
/// </summary>
/// <param name="objectName"></param>
/// <param name="localFilename"></param>
/// filename和uploadid同时相同时 那么在oss碎片管理中代表同一个文件
public void Multipar_tUp(string key,string localFilename,string uploadid=null)
{
try
{
long toatalfilecount = parttotalcount(localFilename);
List<PartETag> partETags = new List<PartETag>();
partETags = partETagsback;
int lastcount = 0;
if (string.IsNullOrEmpty(uploadid))
{ uploadid = UploadID(key, objectName); uploadidback = uploadid; }
else
lastcount = partendlast(objectName, uploadid);
if (!string.IsNullOrEmpty(objectName) || toatalfilecount != 0)
{
if (MultiUploadfile(localFilename, toatalfilecount, partSize, objectName, uploadid, lastcount, ref partETags))
{
if (partendlast(objectName, uploadid) == toatalfilecount)
{
CompleteUploadfile(partETags, objectName, uploadid);
form.Invoke(new Action(() =>
{
form.uploadflog = true;
form.button3.Enabled = false;
form.button4.Enabled = false;
}));
MessageBox.Show("文件上传成功");
}
else
MessageBox.Show("文件上传发生故障");
}
}
else
MessageBox.Show("初始化失败");
}
catch (Exception ex)
{
Form1.stopupload = false;
MessageBox.Show(ex.Message.ToString(), "传输中断");
}
}
/// <summary>
/// 新建并获取uplodid
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
private string UploadID(string key,string objectName)
{
try
{
// 定义上传文件的名字和所属存储空间。在InitiateMultipartUploadRequest中,可以设置ObjectMeta,但不必指定其中的ContentLength。
var request = new InitiateMultipartUploadRequest(bucketName, objectName);
var result = client.InitiateMultipartUpload(request);
return result.UploadId;
}
catch (Exception ex)
{
return null;
}
}
/// <summary>
/// 计算当前要上传的文件需要分成多少part
/// </summary>
/// <param name="localFilename"></param>
/// <returns></returns>
private long parttotalcount(string localFilename)
{
// 计算分片总数。
var fi = new FileInfo(localFilename);
var fileSize = fi.Length;
var partCount = fileSize / partSize;
if (fileSize % partSize != 0)
{
partCount++;
}
return partCount;
}
// 开始分片上传。partETags是保存partETag的列表,OSS收到用户提交的分片列表后,会逐一验证每个分片数据的有效性。 当所有的数据分片通过验证后,OSS会将这些分片组合成一个完整的文件。
//如果是之前上传过的话,那么从原来的位置开始添加。即查找原来的结尾i
private bool MultiUploadfile(string localFilename, long partCount,long partSize,string objectName,string uploadId,int startcount,ref List<PartETag> partETags)
{
try
{
using (var fs = File.Open(localFilename, FileMode.Open))
{
for (var i = startcount; i < partCount; i++)
{
if (Form1.stopupload)
{
startcountback = i;
var skipBytes = (long)partSize * i;
// 定位到本次上传起始位置。
fs.Seek(skipBytes, 0);
// 计算本次上传的片大小,最后一片为剩余的数据大小。
var size = (partSize < fs.Length - skipBytes) ? partSize : (fs.Length - skipBytes);
var request = new UploadPartRequest(bucketName, objectName, uploadId)
{
InputStream = fs,
PartSize = size,
PartNumber = i + 1
};
// 调用UploadPart接口执行上传功能,返回结果中包含了这个数据片的ETag值。
var result = client.UploadPart(request);
partETags.Add(result.PartETag);
partETagsback = partETags;
form.Invoke(new Action(() =>
{
form.progressBar1.Minimum = 0;
form.progressBar1.Maximum = (int)partCount;
form.progressBar1.Value = i + 1;
form.button4.Enabled = true;
form.button3.Enabled = false;
}));
}
else
{
MessageBox.Show("当前传送已经暂停");
form.Invoke(new Action(() =>
{
form.button4.Enabled = false;
form.button3.Enabled = true;
}));
break;
}
}
}
if (partETags.Count == partCount)
return true;
else
return false;
}
catch (Exception ex)
{
throw ex;
}
// 列举已上传的分片。
}
private void CompleteUploadfile(List<PartETag> partETags,string objectName,string uploadId)
{
// 完成分片上传。
try
{
var completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, objectName, uploadId);
foreach (var partETag in partETags)
{
completeMultipartUploadRequest.PartETags.Add(partETag);
}
var result = client.CompleteMultipartUpload(completeMultipartUploadRequest);
Console.WriteLine("complete multi part succeeded");
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 查找未完成的上传 进行到哪一个partcount
/// </summary>
/// <param name="objectName"></param>
/// <param name="uploadId"></param>
/// <returns></returns>
private int partendlast(string objectName,string uploadId)
{
try
{
var listPartsRequest = new ListPartsRequest(bucketName, objectName, uploadId);
var listPartsResult = client.ListParts(listPartsRequest);
return listPartsResult.NextPartNumberMarker;
// Console.WriteLine("List parts succeeded");
// 遍历所有分片。
//var parts = listPartsResult.Parts;
//foreach (var part in parts)
//{
// Console.WriteLine("partNumber: {0}, ETag: {1}, Size: {2}", part.PartNumber, part.ETag, part.Size);
//}
//return listPartsResult.Parts.Count();
}
catch (Exception ex)
{
throw ex;
}
}
}
}
以上就是oss的大文件传输啦 啦啦啦啦啦啦啦 如果运行有问题可以给我留言
还有一点要特别注意就是要是文件传输中断的话会在阿里云的oss存储库里看到上传的碎片,要是几百上个碎片都残留的话 不就是浪费了嘛。但是我没有看到碎片代码处理的方式,只看到的手动的操作 还是有点觉得可惜。当然好像可以手动设置定时自动清理。