首页>热点 > 正文

.Net Web API 006 Controller上传大文件

2023-08-07 20:09:43    出处:博客园

1、上传大文件的方式

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

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


(资料图)

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

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

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

2、上传大文件API实现

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

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

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

/// /// 开始大上传文件/// /// [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);}

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

/// /// 上传大文件/// /// /// [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();}

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

/// /// 结束上传大文件/// /// [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();        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(myFileEntity)        };        await myHttpClientEx.PostAsync();        if (myHttpClientEx.IsSuccess == false)        {            myFileStream.Close();            MessageBox.Show("上传文件失败," + myHttpClientEx.ResponseContenString);            return;        }        myFileStream.Close();        myFileEntity = myHttpClientEx.GetResponseObject();        var myEntityJosnString = JsonSerializer.Serialize(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代码没有实际测试,只是一个思路。

关键词:

消费
产业
房屋财产被淹,中图网遭“毁灭打击”!保险怎么止损? 一场台风,带来北京140年来的最大降雨,也引发京冀多地洪水、泥石流、
王继兴著《片语集》(连载)第二辑、友情题赠 题党瑜华著《异形心电图图谱》 题党瑜华著《异形心电图图谱》。破解心脏疾患之图谱;。培育临床名医的
以政领财提站位 以财辅政促发展 以政领财提站位以财辅政促发展——访市财政局党组书记、局长朱光洪“省
基金