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