.Net Web API 005 Controller上传小文件

1、附属文件对象定义

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

/// <summary>
/// 附属文件实体对象
/// </summary>
public class AttachedFileEntity
{

    /// <summary>
    /// 实体对象GUID
    /// </summary>
    public string GUID { get; set; } = "";

    /// <summary>
    /// 所属对象的GUID
    /// </summary>
    public string EntityGUID { get; set; } = "";

    /// <summary>
    /// 名称
    /// </summary>
    public string Name { get; set; } = "";

    /// <summary>
    /// 关键字
    /// </summary>
    public string KeyWord { get; set; } = "";

    /// <summary>
    /// 文件大小
    /// </summary>
    public int FileSize { get; set; } = 0;

    /// <summary>
    /// 服务器存储路径
    /// </summary>
    public string ServerPath { get; set; } = "";

    /// <summary>
    /// 描述信息
    /// </summary>
    public string Description { get; set; } = "";

}

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

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

2、小文件上传服务

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

代码如下所示。

 /// <summary>
/// 上传文件
/// </summary>
/// <param name="pEntity"></param>
/// <returns></returns>
[HttpPost]
[Route("UploadFile")]
public IActionResult UploadFile()
{
    //获取客户端传来的数据
    var myEntityJosnString = Request.Form["pEntity"].ToString();
    var myEntity = JsonSerializer.Deserialize<AttachedFileEntity>(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<AttachedFileEntity>();
var myEntityJosnString = JsonSerializer.Serialize(myEntity);
MessageBox.Show(myEntityJosnString);

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

/// <summary>
/// HttpClient的自定义扩展类
/// </summary>
public class HttpClientEx
{

    /// <summary>
    /// HttpClient的自定义扩展类
    /// </summary>
    /// <param name="pHttpClient"></param>
    public HttpClientEx(HttpClient? pHttpClient)
    {
        this.HttpClient = pHttpClient;
        this.ParameterDictionary = new Dictionary<string, string>();
    }

    /// <summary>
    /// HttpClient对象
    /// </summary>
    public HttpClient? HttpClient { get; private set; }

    /// <summary>
    /// 服务地址
    /// </summary>
    public string Url { get; set; } = "";

    /// <summary>
    /// 参数字典
    /// </summary>
    public Dictionary<string, string> ParameterDictionary { get; private set; }

    /// <summary>
    /// 请求内容
    /// </summary>
    public HttpContent? HttpContent { get; set; }

    /// <summary>
    /// 请求返回的消息
    /// </summary>
    public HttpResponseMessage? ResponseMessage { get; private set; }

    /// <summary>
    /// 是否执行成功
    /// </summary>
    public bool IsSuccess { get; private set; }

    /// <summary>
    /// 返回的内容字符串
    /// </summary>
    public string ResponseContenString { get; private set; } = "";

    /// <summary>
    /// Get
    /// </summary>
    /// <returns></returns>
    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();
    }

    /// <summary>
    /// Get
    /// </summary>
    /// <returns></returns>
    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();
    }

    /// <summary>
    /// 得到返回的对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public T? GetResponseObject<T>()
    {
        if (this.ResponseContenString == "")
        {
            return default;
        }
        var myJsonSerializerOptions = new JsonSerializerOptions()
        {
            PropertyNameCaseInsensitive = true
        };
        return JsonSerializer.Deserialize<T>(this.ResponseContenString, myJsonSerializerOptions);
    }

    /// <summary>
    /// 得到带参数的Url
    /// </summary>
    /// <returns></returns>
    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]);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章