اخبار، مطالب و رویدادهای مرتبط با توسعه نرم افزار رادکام

کار با فرم های Html و آپلود فایل توسط Web API

کار با فرم های Html و آپلود فایل توسط Web API

آپلود فایل با Web API

در این مقاله ما خواهیم دید که چگونه می توانیم از طریق Web API یک فایل را آپلود کنیم و چگونه می توانیم دیتای multipart MIME را پردازش کنیم.
ابتدا فرم زیر را در نظر می گیریم که شامل کد های Html برای آپلود فایل نیز هست.

<form name="form1" method="post" enctype="multipart/form-data" action="api/upload">
    <div>
        <label for="caption">Image Caption</label>
        <input name="caption" type="text" />
    </div>
    <div>
        <label for="image1">Image File</label>
        <input name="image1" type="file" />
    </div>
    <div>
        <input type="submit" value="Submit" />
    </div>
</form>

خروجی فرم به شکل زیر خواهد بود:

FormOutput

ابن فرم شامل یک کنترل text و یک کنترل file است. زمانی که یک فرم دارای کنترل file باشد، ویژگی enctype فرم باید همیشه "multipart/form-data" باشد، و این بدین معنی است که محتوای فرم باید به عنوان یک پیام multipart MIME به سمت سرور ارسال شود.
فرمت و قالب یک پیام miltupart MIME به صورت زیر است، شاید با بررسی و دقت در مثال زیر راحت تر بتوانیم مفهوم multipart MIME را درک کنیم.

POST http://localhost:50460/api/values/1 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------41184676334
Content-Length: 29278

-----------------------------41184676334
Content-Disposition: form-data; name="caption"

Summer vacation
-----------------------------41184676334
Content-Disposition: form-data; name="image1"; filename="GrandCanyon.jpg"
Content-Type: image/jpeg

(Binary data not shown)
-----------------------------41184676334--

 این پیام به دو قسمت تقسیم می شود.  یکی برای کنترل های فرم  و دیگری برای boundry های که با خطوط ممتد مشخص شده اند.
بخش boundry شامل یک کامپوننت تصادفی ("41184676334") است که این اطمینان را به برنامه می دهد که عبارت boundry در داخل خود بخش پیام به صورت اتفاقی ایجاد نشده باشد.

هر بخش از پیام شامل حد اقل یک هدر است. که در ادامه آن هدر ها محتوای هر بخش قرار دارد.
  • هدر  Content-Disposition  حاوی نام کنترل است. برای فایل ها حاوی نام فایل است.
  • هدر Content-Type خود اطلاعات را در این بخش مشخص می کند. اگر این هدر حذف گردد،  اطلاعات با حالت پیش فرض text/plain ارسال می شوند.

در مثال فوق، کاربر فایلی را با نام GrandCanyon.jpg با نوع محتوای image/jpeg آپلود کرده است. و همچنین مقدار کنترل text فرم "Summer Vacation" است.

آپلود فایل

حال زمان آن رسیده است که نگاهی به کنترلر Web API ی بیندازیم که محتوای فایل  ها را از یک پیام multipart MIME می خواند. این کنترلر فایل ها را به صورت آسنکرون خواهد خواند. Web API از متد های آسنکرون بر مبنای مدل برنامه نویسی وظیفه محور (task-based programming model) پشتیبانی می کند. کد زیر را مشاهده کنیم که بر مبنای .Net FrameWork 4.5 نوشته شده است که از متد های await و async پشتیبانی می کند.

using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;

public class UploadController : ApiController
{
    public async Task<HttpResponseMessage> PostFormData()
    {
        // Check if the request contains multipart/form-data.
        if (!Request.Content.IsMimeMultipartContent())
        {
            throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
        }

        string root = HttpContext.Current.Server.MapPath("~/App_Data");
        var provider = new MultipartFormDataStreamProvider(root);

        try
        {
            // Read the form data.
            await Request.Content.ReadAsMultipartAsync(provider);
           
            // This illustrates how to get the file names.
            foreach (MultipartFileData file in provider.FileData)
            {
                Trace.WriteLine(file.Headers.ContentDisposition.FileName);
                Trace.WriteLine("Server file path: " + file.LocalFileName);
            }
            return Request.CreateResponse(HttpStatusCode.OK);
        }
        catch (System.Exception e)
        {
            return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
        }
    }
}
همانطور که مشاهده می فرمایید متد داخل کنترلر هیچ پارامتری را به عنوان ورودی نمی گیرد. و دلیل آن این است که متد مربوطه بدنه درخواست را داخل خودِ action پردازش می کند، بدون آنکه  یک media-type formatter ای را فراخوانی کرده باشد.
متد IsMultipartContent بررسی می کند  که آیا درخواست شامل یک multipart MIME message هست یا نه. اگر جواب منفی بود، کنترلر  کد خطای 415 را برمی گرداند. و این کد به معنی MediaType غیر قابل پشتیبانی است.
کلاس MultipartFormDataStreamProvider یک شیء کمکی است که   وظیفه تخصیص file Streem های لازم را به فایل های آپلود شده به عهده دارد. برای خواندن یک پیام از نوع multipart MIME کافی است که متد ReadAsMultipartAsync فراخوانی شود.این متد تمام قسمت های پیام را  استخراج کرده و آنها را داخل استریم هایی که توسط  MultipartFormDataStreamProvider ایجاد می شوند می نویسد.
زمانی که کار متد تمام شد، قادر خواهیم بود اطلاعات مورد نیاز خود در مورد فایل ها را از مشخصه FileData دریافت کنیم، که یک مجموعه از  شیء MultipartFileData است.
  • MultipartFileData.FileName نام فایل روی سرور است، جایی که فایل در آن ذخیره شده است.
  • MultipartFileData.Headers شامل بخش هدر است(منظور هدر درخواست نیست). شما از این می توانید برای دسترسی به هدر های Content_Disposition  و Content-Type استفاده کنید.
همانگونه که از نامش پیداست، ReadAsMultipartAsync  یک متد آسنکرون است. برای انجام کار مورد نظر خود بعد از اتمام کار متد،  از continuation task و یا از کلمه کلیدی await استفاده نمایید.
نسخه منطبق بر .Net Framework 4.0 کد قبلی را در زیر مشاهده می کنید:

public Task<HttpResponseMessage> PostFormData()
{
    // Check if the request contains multipart/form-data.
    if (!Request.Content.IsMimeMultipartContent())
    {
        throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
    }

    string root = HttpContext.Current.Server.MapPath("~/App_Data");
    var provider = new MultipartFormDataStreamProvider(root);

    // Read the form data and return an async task.
    var task = Request.Content.ReadAsMultipartAsync(provider).
    ContinueWith<HttpResponseMessage>(t =>
    {
        if (t.IsFaulted || t.IsCanceled)
        {
            Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
        }

        // This illustrates how to get the file names.
        foreach (MultipartFileData file in provider.FileData)
        {
            Trace.WriteLine(file.Headers.ContentDisposition.FileName);
            Trace.WriteLine("Server file path: " + file.LocalFileName);
        }
        return Request.CreateResponse(HttpStatusCode.OK);
    });

    return task;
}



خواندن اطلاعات کنترل های داخل فرم

فرمی که پیشتر نشان دادیم شامل یک کنترل textbox یا همان input بود

<div>
    <label for="caption">Image Caption</label>
    <input name="caption" type="text" />
</div>

شما قادر هستید مقادیر کنترل های داخل فرم را از ویژگی FormData مربوط به  MultipartFormDataStreamProvider دریافت کنید.

public async Task<HttpResponseMessage> PostFormData()
{
    if (!Request.Content.IsMimeMultipartContent())
    {
        throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
    }

    string root = HttpContext.Current.Server.MapPath("~/App_Data");
    var provider = new MultipartFormDataStreamProvider(root);

    try
    {
        await Request.Content.ReadAsMultipartAsync(provider);

        // Show all the key-value pairs.
        foreach (var key in provider.FormData.AllKeys)
        {
            foreach (var val in provider.FormData.GetValues(key))
            {
                Trace.WriteLine(string.Format("{0}: {1}", key, val));
            }
        }

        return Request.CreateResponse(HttpStatusCode.OK);
    }
    catch (System.Exception e)
    {
        return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
    }
}

FormData یک NameValueCollection است که شامل جفت های name/value از کنترل های داخل فرم است. این کالکشن می تواند شامل کلید های تکراری نیز باشد. فرم زیر را در نظر بگیرید.

<form name="trip_search" method="post" enctype="multipart/form-data" action="api/upload">
    <div>
        <input type="radio" name="trip" value="round-trip"/>
            Round-Trip
    </div>
    <div>
        <input type="radio" name="trip" value="one-way"/>
        One-Way
    </div>
    <div>
        <input type="checkbox" name="options" value="nonstop" />
        Only show non-stop flights
    </div>
    <div>
        <input type="checkbox" name="options" value="airports" />
        Compare nearby airports
    </div>
    <div>
        <input type="checkbox" name="options" value="dates" />
        My travel dates are flexible
    </div>
    <div>
        <label for="seat">Seating Preference</label>
        <select name="seat">
            <option value="aisle">Aisle</option>
            <option value="window">Window</option>
            <option value="center">Center</option>
            <option value="none">No Preference</option>
        </select>
    </div>
</form>

Form Output

بدنه درخواست به احتمال زیاد چنین خواهد بود:

-----------------------------7dc1d13623304d6
Content-Disposition: form-data; name="trip"

round-trip
-----------------------------7dc1d13623304d6
Content-Disposition: form-data; name="options"

nonstop
-----------------------------7dc1d13623304d6
Content-Disposition: form-data; name="options"

dates
-----------------------------7dc1d13623304d6
Content-Disposition: form-data; name="seat"

window
-----------------------------7dc1d13623304d6--

با توجه به انتخاب های انجام شده داخل فرم، کالکشن FormData شامل جفت های key/value زیر خواهند بود.

  • trip: round-trip
  • options: nonstop
  • options: dates
  • seat: window


پست های مرتبط

نام را وارد کنید
تعداد کاراکتر باقیمانده: 1000
نظر خود را وارد کنید