• .Net Web API 005 Controller上传小文件


    1、附属文件对象定义

    一般情况下,系统里面的文件都会附属一个对象存在,例如用户的头像文件,会附属用户对象存在。邮件中的文件会附属邮件存在。所以在系统里面,我们会创建一个附属文件对象,命名为AttachedFileEntity。其定义如下所示。

    复制代码
    /// 
    /// 附属文件实体对象
    /// 
    public class AttachedFileEntity
    {
    
        /// 
        /// 实体对象GUID
        /// 
        public string GUID { get; set; } = "";
    
        /// 
        /// 所属对象的GUID
        /// 
        public string EntityGUID { get; set; } = "";
    
        /// 
        /// 名称
        /// 
        public string Name { get; set; } = "";
    
        /// 
        /// 关键字
        /// 
        public string KeyWord { get; set; } = "";
    
        /// 
        /// 文件大小
        /// 
        public int FileSize { get; set; } = 0;
    
        /// 
        /// 服务器存储路径
        /// 
        public string ServerPath { get; set; } = "";
    
        /// 
        /// 描述信息
        /// 
        public string Description { get; set; } = "";
    
    }
    复制代码

    EntityGUID属性的作用是,定义该文件属于哪个实体对象,例如某个用户的头像文件,该属性就是这个用户对象的GUID值。

    KeyWord属性用来标识文件。例如UserEntity有两个文件,头像和一个自我介绍的视频文件。这两个文件的EntityGUID都是UserEntity的GUID,那么就可以通过KeyWord来区分两个文件是做什么用的。

    2、小文件上传服务

    如果一个文件比较小,例如3M以内,那么我们就可以一次性把文件上传上来,上传的时候,要把AttachedFileEntity对象传进来,并添加到数据库中。

    代码如下所示。

    复制代码
     /// 
    /// 上传文件
    /// 
    /// 
    /// 
    [HttpPost]
    [Route("UploadFile")]
    public IActionResult UploadFile()
    {
        //获取客户端传来的数据
        var myEntityJosnString = Request.Form["pEntity"].ToString();
        var myEntity = JsonSerializer.Deserialize(myEntityJosnString);
        var myFile = Request.Form.Files[0];
    
        //设置新的文件路径
        string myFileEx = Path.GetExtension(myFile.FileName);
        string myServerFilePath = DateTime.Now.ToString("yyyy_MM_dd") + "\\" + Guid.NewGuid().ToString() + myFileEx;
        myEntity!.ServerPath = myServerFilePath;
    
        //创建目录
        string myFullServerPath = AppDomain.CurrentDomain.BaseDirectory + "\\Files\\" + myServerFilePath;
        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.Create);
            myBinaryWriter = new BinaryWriter(myFileStream);
            myBinaryWriter.Write(myBytes);
        }
        finally
        {
            myBinaryWriter?.Close();
            myFileStream?.Close();
            myStream?.Close();
        }
    
        //把附属文件对象保存到数据库中
        //代码略
    
        return this.Ok(myEntity);
    }
    复制代码

    因为我们要传入两个复杂的对象AttachedFileEntity和File,所以就不能用参数接了,就需要用代码从Request里面读取。文件其本质就是二进制数据,我们获取这个二进制之后,把数据保存成文件就可以了。然后把pEntity写入到数据库中。

    3、前端调用

    先用桌面端测试,界面是用C#写的WPF桌面软件,入下图所示。

    截图.png

    调用代码入下所示。

    复制代码
    var myFilePath = this.UI_SmallFile_TextBox.Text.Trim();
    if (myFilePath.Length == 0)
    {
        MessageBox.Show("请选择一个文件。");
        return;
    }
    if (File.Exists(myFilePath) == false)
    {
        MessageBox.Show("文件不存在,请重新选择。");
        return;
    }
    
    //定义AttachedFileEntity
    var myAttachedFileEntity = new AttachedFileEntity()
    {
        GUID = Guid.NewGuid().ToString(),
        Name = "用户头像",
        KeyWord = "UserProfilePhoto",
        Description = "",
        EntityGUID = "AAAA"
    };
    
    //定义请求内容
    var myFileStream = new FileStream(myFilePath, FileMode.Open);
    myAttachedFileEntity.FileSize = (int)myFileStream.Length;
    var myFileName = Path.GetFileName(myFilePath);
    var myFileStreamContent = new StreamContent(myFileStream);
    var myMultipartFormDataContent = new MultipartFormDataContent
    {
        { JsonContent.Create(myAttachedFileEntity), "pEntity" },
        { myFileStreamContent, "pFormFile", myFileName }
    };
    
    //请求服务
    var myHttpClientEx = new HttpClientEx(new HttpClient())
    {
        Url = "http://localhost:5000/api/AttachedFile/UploadFile",
        HttpContent = myMultipartFormDataContent
    };
    await myHttpClientEx.PostAsync();
    myFileStream.Close();
    
    //解析结果
    if (myHttpClientEx.IsSuccess == false)
    {
        MessageBox.Show(("上传文件失败," + myHttpClientEx.ResponseContenString));
        return;
    }
    var myEntity = myHttpClientEx.GetResponseObject();
    var myEntityJosnString = JsonSerializer.Serialize(myEntity);
    MessageBox.Show(myEntityJosnString);
    复制代码

    HttpClientEx是对.Net定义的HttpClient一些功能的扩展,这样用起来会比较方便,代码定义如下。

    复制代码
    /// 
    /// HttpClient的自定义扩展类
    /// 
    public class HttpClientEx
    {
    
        /// 
        /// HttpClient的自定义扩展类
        /// 
        /// 
        public HttpClientEx(HttpClient? pHttpClient)
        {
            this.HttpClient = pHttpClient;
            this.ParameterDictionary = new Dictionary<string, string>();
        }
    
        /// 
        /// HttpClient对象
        /// 
        public HttpClient? HttpClient { get; private set; }
    
        /// 
        /// 服务地址
        /// 
        public string Url { get; set; } = "";
    
        /// 
        /// 参数字典
        /// 
        public Dictionary<string, string> ParameterDictionary { get; private set; }
    
        /// 
        /// 请求内容
        /// 
        public HttpContent? HttpContent { get; set; }
    
        /// 
        /// 请求返回的消息
        /// 
        public HttpResponseMessage? ResponseMessage { get; private set; }
    
        /// 
        /// 是否执行成功
        /// 
        public bool IsSuccess { get; private set; }
    
        /// 
        /// 返回的内容字符串
        /// 
        public string ResponseContenString { get; private set; } = "";
    
        /// 
        /// Get
        /// 
        /// 
        public async Task GetAsync()
        {
            var myUrlWithParameters = this.GetUrlWithParameters();
            this.ResponseMessage = await this.HttpClient!.GetAsync(myUrlWithParameters);
            this.IsSuccess = this.ResponseMessage.IsSuccessStatusCode;
            this.ResponseContenString = await this.ResponseMessage.Content.ReadAsStringAsync();
        }
    
        /// 
        /// Get
        /// 
        /// 
        public async Task PostAsync()
        {
            var myUrlWithParameters = this.GetUrlWithParameters();
            this.ResponseMessage = await this.HttpClient!.PostAsync(myUrlWithParameters, this.HttpContent);
            this.IsSuccess = this.ResponseMessage.IsSuccessStatusCode;
            this.ResponseContenString = await this.ResponseMessage.Content.ReadAsStringAsync();
        }
    
        /// 
        /// 得到返回的对象
        /// 
        /// 
        /// 
        public T? GetResponseObject()
        {
            if (this.ResponseContenString == "")
            {
                return default;
            }
            var myJsonSerializerOptions = new JsonSerializerOptions()
            {
                PropertyNameCaseInsensitive = true
            };
            return JsonSerializer.Deserialize(this.ResponseContenString, myJsonSerializerOptions);
        }
    
        /// 
        /// 得到带参数的Url
        /// 
        /// 
        private string GetUrlWithParameters()
        {
            if (this.ParameterDictionary == null)
            {
                return this.Url;
            }
            if (this.ParameterDictionary.Count == 0)
            {
                return this.Url;
            }
    
            var myParameterList = new List<string>();
            foreach (var myItem in this.ParameterDictionary)
            {
                myParameterList.Add(myItem.Key + "=" + myItem.Value);
            }
            return this.Url + "?" + string.Join("&", myParameterList);
        }
    
    }
    复制代码

    如果客户端是Js,就需要自己组织服务需要的数据了。代码入下所示。

    复制代码
    var myFileReader = new FileReader();
    var myFileName = "";
    
    myFileReader.onloadend = function () {
        var myFileResult = myFileReader.result;
        var myFileLength = myFileResult.byteLength;
    
        var myFileEntity = new Object()
        {
            ServerPath: ""
        };
        Upload();
    
        function Upload() {
    
            var myByteArray = myFileResult.slice(0, myFileLength);
            var myBlob = new Blob([myByteArray]);
            var myFile = new File([myBlob], myFileName);
            var myFormData = new FormData();
            myFormData.append("file", myFile)
            myFormData.append("pEntity", json.stringify(myFileEntity));
            request.post(myUrl, {
                data: myFormData
            }).then(function (data) {
                myFileEntity = json.parse(data);
                alert("上传文件结束。");
                alert(json.stringify(myFileEntity));
            }, function (err) {
                alert(err);
                return;
            });
        }
    }
    
    myFileName = this.files[0].name;
    myFileReader.readAsArrayBuffer(this.files[0]);
    复制代码
  • 相关阅读:
    [极客大挑战 2019]Http
    一个基于NetCore开发的前后端分离CMS系统
    面向对象oop的理解
    ubuntu提高 github下载速度
    峰会实录 | StarRocks存储引擎近期进展与实时分析实践
    springboot517基于SpringBoot+Vue的高校线上心理咨询室的设计与实现-手把手调试搭建
    STM32WB55开发(3)----断开蓝牙连接
    疫苗预约系统毕业设计,疫苗预约系统设计源码,疫苗预约系统开题报告需求分析
    动手学深度学习:1.线性回归从0开始实现
    HTML5网页设计制作基础大二dreamweaver作业、使用HTML+CSS技术制作博客网站(5个页面)
  • 原文地址:https://www.cnblogs.com/mytudousi/p/17608801.html