1、上传大文件的方式

上传大文件就需要一段一段的上传,主要是先在客户端获取文件的大小,例如想一次传256kb,那就按照256kb分割。分割后又两种上传方式。

(1)逐个数据段读取,然后调用API上传,把数据追加到文件上。上传完这一段,接着传下一段,直到上传完毕。

(2)方式与(1)类似,只是可以好几段可以并行上传,上传后,会把每段按照索引命名,在服务器上保存成临时文件。判断都上传完毕后,再调用合并命令,把这些小的碎文件,合并成大的目标文件。

我一般喜欢用第一种方式,主要是因为简单明了。缺点就是,因为必须等上一段完成后,再传下一段,所以无法并行上传,导致速度会受到影响。

但我参与的项目基本上都是在局域网运行并且使用人数有限,所以这种传大文件的方式是可以满足系统要求。

2、上传大文件API实现

下面的实现用到了定义的AttachedFileEntity和HttpClientEx,这两个类的定义可参考 005 Controller上传小文件

分三步,开始上传、循环上传二进制段、结束上传。

开始上传API的目的是为了获取文件在服务器上的存储路径,代码如下。

/// <summary>
/// 开始大上传文件
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("StartUploadBigFile")]
public IActionResult StartUploadBigFile(AttachedFileEntity pEntity, string pFileEx)
{
string myServerFilePath = DateTime.Now.ToString("yyyy_MM_dd") + "\\" + Guid.NewGuid().ToString() + pFileEx;
pEntity.ServerPath = myServerFilePath;
return this.Ok(pEntity);
}

分段上传文件,接收到后,追加到现有的文件上。

/// <summary>
/// 上传大文件
/// </summary>
/// <param name="pServerPath"></param>
/// <returns></returns>
[HttpPost]
[Route("UploadBigFile")]
[DisableRequestSizeLimit]
public IActionResult UploadBigFile(string pServerPath)
{
var myFile = Request.Form.Files[0]; //创建目录
string myFullServerPath = AppDomain.CurrentDomain.BaseDirectory + "\\Files\\" + pServerPath;
string myFullFolder = Path.GetDirectoryName(myFullServerPath)!;
if (Directory.Exists(myFullFolder) == false)
{
Directory.CreateDirectory(myFullFolder);
} //写入文件
Stream? myStream = null;
FileStream? myFileStream = null;
BinaryWriter? myBinaryWriter = null;
try
{
myStream = myFile.OpenReadStream();
byte[] myBytes = new byte[myStream.Length];
myStream.Read(myBytes, 0, myBytes.Length);
myStream.Seek(0, SeekOrigin.Begin); myFileStream = new FileStream(myFullServerPath, FileMode.Append);
myBinaryWriter = new BinaryWriter(myFileStream);
myBinaryWriter.Write(myBytes);
}
catch (Exception ex)
{
return this.BadRequest("上传大文件失败," + ex.Message);
}
finally
{
myBinaryWriter?.Close();
myFileStream?.Close();
myStream?.Close();
} return this.Ok();
}

最后,结束上传,把文件的信息记录到数据库中。

/// <summary>
/// 结束上传大文件
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("FinishUploadBigFile")]
public IActionResult FinishUploadBigFile(AttachedFileEntity pEntity)
{
if (string.IsNullOrEmpty(pEntity.GUID))
{
pEntity.GUID = Guid.NewGuid().ToString();
}
//记录到数据库中
//代码略
return this.Ok(pEntity);
}

3、客户端调用

客户端如果用的C#,代码中没有加入进度信息,进度信息可以传入一个ProcessInfo对象,传一段数据后,就更新下进度信息。

调用的代码入下所示。

private void Init_BigFileUpLoad_UIs()
{
this.UI_BigFile_Button.Click += (x, y) =>
{
var myOpenFileDialog = new OpenFileDialog
{
Filter = ".*|*.*"
};
var myIsOK = myOpenFileDialog.ShowDialog();
if (myIsOK != true)
{
return;
}
this.UI_BigFile_TextBox.Text = myOpenFileDialog.FileName;
}; this.UI_BigFileUpLoad_Button.Click += async (x, y) =>
{
var myFilePath = this.UI_BigFile_TextBox.Text.Trim();
if (myFilePath.Length == 0)
{
MessageBox.Show("请选择一个文件。");
return;
}
if (File.Exists(myFilePath) == false)
{
MessageBox.Show("文件不存在,请重新选择。");
return;
} //定义AttachedFileEntity
var myFileEntity = new AttachedFileEntity()
{
GUID = Guid.NewGuid().ToString(),
Name = "用户头像",
KeyWord = "UserProfilePhoto",
Description = "",
EntityGUID = "AAAA"
}; //打开上传的文件
var myFileStream = new FileStream(myFilePath, FileMode.Open);
myFileEntity.FileSize = (int)myFileStream.Length;
var myFileName = Path.GetFileName(myFilePath); //每次上传256kb
int myChunkSize = 1024 * 256;
int myChunkCount = (int)Math.Ceiling(myFileStream.Length / (double)myChunkSize); //调用开始上传
var myHttpClient = new HttpClient();
var myHttpClientEx = new HttpClientEx(myHttpClient)
{
Url = "http://localhost:5000/api/AttachedFile/StartUploadBigFile",
HttpContent = JsonContent.Create(myFileEntity)
};
myHttpClientEx.ParameterDictionary.Add("pFileEx", Path.GetExtension(myFileName));
await myHttpClientEx.PostAsync();
if (myHttpClientEx.IsSuccess == false)
{
myFileStream.Close();
MessageBox.Show("上传文件失败," + myHttpClientEx.ResponseContenString);
return;
}
myFileEntity = myHttpClientEx.GetResponseObject<AttachedFileEntity>();
if (myFileEntity == null)
{
myFileStream.Close();
MessageBox.Show("上传文件失败,返回的AttachedFileEntity为null。");
return;
} //循环上传文件
for (int i = 0; i < myChunkCount; i++)
{
//组织数据
int myByteArraySize = myChunkSize;
if (i == myChunkCount - 1)
{
myByteArraySize = (int)(myFileStream.Length % myChunkSize);
}
byte[] myBytes = new byte[myByteArraySize];
myFileStream.Position = myChunkSize * i;
myFileStream.Read(myBytes, 0, myBytes.Length);
var myMemoryStream = new MemoryStream(myBytes); //请求服务
myHttpClientEx = new HttpClientEx(myHttpClient)
{
Url = "http://localhost:5000/api/AttachedFile/UploadBigFile",
HttpContent = new MultipartFormDataContent
{
{new StreamContent(myMemoryStream),"pFile",myFileName}
}
};
myHttpClientEx.ParameterDictionary.Add("pServerPath", myFileEntity!.ServerPath);
await myHttpClientEx.PostAsync(); //解析结果
if (myHttpClientEx.IsSuccess == false)
{
myFileStream.Close();
MessageBox.Show("上传文件失败," + myHttpClientEx.ResponseContenString);
return;
}
} //结束上传
myHttpClientEx = new HttpClientEx(myHttpClient)
{
Url = "http://localhost:5000/api/AttachedFile/FinishUploadBigFile",
HttpContent = JsonContent.Create<AttachedFileEntity>(myFileEntity)
};
await myHttpClientEx.PostAsync();
if (myHttpClientEx.IsSuccess == false)
{
myFileStream.Close();
MessageBox.Show("上传文件失败," + myHttpClientEx.ResponseContenString);
return;
}
myFileStream.Close();
myFileEntity = myHttpClientEx.GetResponseObject<AttachedFileEntity>();
var myEntityJosnString = JsonSerializer.Serialize<AttachedFileEntity>(myFileEntity);
MessageBox.Show(myEntityJosnString);
};
}

如果客户端是js,代码如下。

on(myButton, "change", function (e) {
var myFileReader = new FileReader();
var myFileName = ""; myFileReader.onloadend = function () {
var myFileResult = myFileReader.result;
var myFileLength = myFileResult.byteLength; var myPerLength = 1024 * 256;
var myCount = Math.ceil(myFileLength / myPerLength); var myFileEntity = new Object()
{
ServerPath: ""
}; //调用开始上传StartUploadBigFile,具体代码略。 var myK = 0;
Upload(); function Upload() { var myByteArray = myFileResult.slice(myPerLength * myK, myPerLength * (myK + 1));
var myBlob = new Blob([myByteArray]);
var myFile = new File([myBlob], myFileName);
var myFormData = new FormData();
myFormData.append("file", myFile)
request.post(myUrl + "?pServerFile=" + myFileEntity.ServerPath +, {
data: myFormData
}).then(function (data) {
myFileEntity = json.parse(data);
myK++;
if (myK < myCount) {
Upload();
}
else {
alert("上传大文件结束。");
alert(json.stringify(myFileEntity));
//结束,post FinishUploadBigFile
} }, function (err) {
alert(err);
return;
});
}
} myFileName = this.files[0].name;
myFileReader.readAsArrayBuffer(this.files[0]);
});

Js代码没有实际测试,只是一个思路。

.Net Web API 006 Controller上传大文件的更多相关文章

  1. WEB上传大文件解决方案

    众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路.下面贴出简易 ...

  2. 【Web应用】JAVA网络上传大文件报500错误

    问题描述 当通过 JAVA 网站上传大文件,会报 500 错误. 问题分析 因为 Azure 的 Java 网站都是基于 IIS 转发的,所以我们需要关注 IIS 的文件上传限制以及 requestT ...

  3. Web上传大文件的解决方案

    需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制. 第一步: 前端修改 由于项目使用的是 ...

  4. WEB上传大文件

    众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路.下面贴出简易 ...

  5. web上传大文件(>4G)有什么解决方案?

    众所皆知,web上传大文件,一直是一个痛.上传文件大小限制,页面响应时间超时.这些都是web开发所必须直面的. 本文给出的解决方案是:前端实现数据流分片长传,后面接收完毕后合并文件的思路. 实现文件夹 ...

  6. asp.net core流式上传大文件

    asp.net core流式上传大文件 首先需要明确一点就是使用流式上传和使用IFormFile在效率上没有太大的差异,IFormFile的缺点主要是客户端上传过来的文件首先会缓存在服务器内存中,任何 ...

  7. [Asp.net]Uploadify上传大文件,Http error 404 解决方案

    引言 之前使用Uploadify做了一个上传图片并预览的功能,今天在项目中,要使用该插件上传大文件.之前弄过上传图片的demo,就使用该demo进行测试.可以查看我的这篇文章:[Asp.net]Upl ...

  8. ASP.NET上传大文件的问题

    原文:http://www.cnblogs.com/wolf-sun/p/3657241.html?utm_source=tuicool&utm_medium=referral 引言 之前使用 ...

  9. QQ上传大文件为什么这么快

    今天和同事在群里讨论“QQ上传大文件/QQ群发送大文件时,可以在极短的时间内完成”是如何做到的. 有时候我们通过QQ上传一个几百M的文件,竟然只用了几秒钟,从带宽上限制可以得出,实际上传文件是不可能的 ...

  10. IIS7下swfupload上传大文件出现404错误

    要求上传附件大小限制在2G,原本以为可以轻松搞定.在编译模式下可以上传大文件,可是在IIS7下(自己架的服务器),一上传大的文件就会出现 Http 404错误,偶尔有的文件还有IO. error错误. ...

随机推荐

  1. #Python实例 计算外卖配送距离(基于百度API接口)---第二篇

    https://www.cnblogs.com/simone331/p/17218019.html 在上一篇中,我们计算了两点的距离(链接为上篇文章),但是具体业务中,往往会存在一次性计算多组,上百甚 ...

  2. react之todoList基础小项目

    1.项目最终成品和项目目录快照如图: 2.context.js文件 // 使用context进行多级传递数据 // 1. createContext 创建一个可以多级传递的context数据 // 2 ...

  3. 2023-04-29:一个序列的 宽度 定义为该序列中最大元素和最小元素的差值。 给你一个整数数组 nums ,返回 nums 的所有非空 子序列 的 宽度之和 由于答案可能非常大,请返回对 109

    2023-04-29:一个序列的 宽度 定义为该序列中最大元素和最小元素的差值. 给你一个整数数组 nums ,返回 nums 的所有非空 子序列 的 宽度之和 由于答案可能非常大,请返回对 109 ...

  4. 2023-04-09:使用 Golang 重写的 ffmpeg 示例encode_video.c,实现视频编码并将编码后的数据封装为容器格式,最终写入输出文件。

    2023-04-09:使用 Golang 重写的 ffmpeg 示例encode_video.c,实现视频编码并将编码后的数据封装为容器格式,最终写入输出文件. 答案2023-04-09: 本文介绍的 ...

  5. 2022-08-31:以下go语言代码输出什么?A:江苏;B:v[“province“]取值错误;C:m.Store存储错误;D:不知道。 package main import ( “fm

    2022-08-31:以下go语言代码输出什么?A:江苏:B:v["province"]取值错误:C:m.Store存储错误:D:不知道. package main import ...

  6. 2022-07-22:以下go语言代码输出什么?A:1;B:1.5;C:编译错误;D:1.49。 package main import “fmt“ func main() { var i

    2022-07-22:以下go语言代码输出什么?A:1:B:1.5:C:编译错误:D:1.49. package main import "fmt" func main() { v ...

  7. 2022-05-16:A -> B,表示A认为B是红人, A -> B -> C,表示A认为B是红人,B认为C是红人,规定“认为”关系有传递性,所以A也认为C是红人, 给定一张有向图,方式是给定M个有

    2022-05-16:A -> B,表示A认为B是红人, A -> B -> C,表示A认为B是红人,B认为C是红人,规定"认为"关系有传递性,所以A也认为C是红 ...

  8. Abaqus结构仿真软件的非线性问题与解决方案

    ​无论是什么FEA 软件,想要获得非线性问题的一些解决方法始终没有那么简单.遇到问题是很常见的,那么下面就来看看Abaqus用户克服这一类问题的解决方法吧. 1. 简化模型 从简化模型开始,通过逐渐添 ...

  9. vue平铺日历组件之按住ctrl、shift键实现跨月、跨年多选日期的功能

    已经好久没有更新过博客了,大概有两三年了吧,因为换了工作,工作也比较忙,所以也就没有时间来写技术博客,期间也一直想写,但自己又比较懒,就给耽误了.今天这篇先续上,下一篇什么时候写,我也不知道,随心所欲 ...

  10. JavaScript模块化 之( Commonjs、AMD、CMD、ES6 modules)演变史

    经常在工作中使用define(['./modulename'],function(modulename){}),require(['modulename'],function(modulename){ ...