FileUpload service with WCF Web Api Preview 5

Topics: Web Api
Sep 24, 2011 at 8:10 AM
Edited Sep 24, 2011 at 8:12 AM

hi,

I got the WebApi.All package installed to my empty web application project and I am trying to implement a fileupload service but I am nowhere at the configuration level so far.

I have the following code so far : 

 

using System.ServiceModel.Web;
using System.Net.Http;
using System.IO;

namespace WCF.FileUpload {

    public class AccommPropertyPicture {

        private readonly string uploadPath = @"C:\website\@cdn\pictures\accomm";

        [WebInvoke(UriTemplate = "AccommPropertyPicture/{accommpropertyid}")]
        public HttpResponseMessage Upload(int accommpropertyid, HttpRequestMessage request) {

            // grab the posted stream  
            Stream stream = request.Content.ContentReadStream;

            string filename = "to-be-implemented-soon";

            // write it to   
            using (FileStream fileStream = File.Create(string.Format(uploadPath, filename), (int)stream.Length)) {

                byte[] bytesInStream = new byte[stream.Length];
                stream.Read(bytesInStream, 0, (int)bytesInStream.Length);
                fileStream.Write(bytesInStream, 0, bytesInStream.Length);
            }

            // http house keeping  
            return new HttpResponseMessage(System.Net.HttpStatusCode.Created);
        }

    }
}

Here is the Application_Start;

 

        protected void Application_Start(object sender, EventArgs e) {

            RouteTable.Routes.MapServiceRoute<AccommPropertyPicture>("accomm");

        }

 

I need to configure TransferMode = TransferMode.Streamed and MaxReceivedMessageSize = 1024 * 1024 * 10 

Should I do that as follows? would that solve my problem? or is there a better way of implementing this feature. I heard there is a built in feature in preview 5 but how can I implement that?

 

        protected void Application_Start(object sender, EventArgs e) {

            var config = new WebApiConfiguration();
            config.MaxReceivedMessageSize = 1024 * 1024 * 10;
            config.TransferMode = System.ServiceModel.TransferMode.Streamed;

            RouteTable.Routes.MapServiceRoute<AccommPropertyPicture>("accomm", config);

        }
Coordinator
Sep 24, 2011 at 9:43 AM

In Preview 5 we provide an HTTP file upload feature. It allows browsers/clients to send MIME multipart/form-data requests which contain attachments and data to the server. On the server you can pull them out using HttpContent, access the collection of attachments as well as the form data.

If you look at the sample code below which is from Preview 5 (in the Microsoft.Net.Http.Formatting.Scenarios.MimeMultipart tests) you can see how to use it. Basically you use the IsMimeMultipartContent extension method to check if the content is multipart. Then you can enumerate through the parts by calling ReadAsMultipartAsync which returns a collection of parts.

     /// <summary>
    /// This sample WCF service reads the contents of an HTML file upload and writes one or more body parts to a local file.
    /// </summary>
    [ServiceContract]
    internal class FileService
    {
        [WebInvoke(UriTemplate = "", Method = "POST")]
        public List<FileResult> UploadFile(HttpRequestMessage request)
        {
            if (!request.Content.IsMimeMultipartContent())
            {
                throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
            }

            // Create a stream provider for setting up output streams
            MultipartFormDataStreamProvider streamProvider = new MultipartFormDataStreamProvider();

            // Read the MIME multipart content using the stream provider we just created.
            var task = request.Content.ReadAsMultipartAsync(streamProvider);
            task.Wait();
            IEnumerable<HttpContent> bodyparts = task.Result;

            // The submitter field is the entity with a Content-Disposition header field with a "name" parameter with value "submitter"
            string submitter;
            if (!bodyparts.TryGetFormFieldValue("submitter", out submitter))
            {
                submitter = "unknown";
            }

            // Get list of local file names from stream provider
            IDictionary<string, string> bodyPartFileNames = streamProvider.BodyPartFileNames;

            // Create response
            return bodyPartFileNames.Select(kv =>
                {
                    FileInfo fileinfo = new FileInfo(kv.Value);
                    return new FileResult
                    {
                        FileName = kv.Key,
                        LocalPath = fileinfo.FullName,
                        LastModifiedTime = fileinfo.LastWriteTimeUtc,
                        Length = fileinfo.Length,
                        Submitter = submitter
                    };
                }).ToList();
        }
    }

You'll notice there is a MultiPartFormDataStreamProvider in that sample. The provider handles automatically persisting any files that are uploaded to disk. You can configure the provider as to the root path, otherwise by default it uses a temp folder.

You don't "need' the provider unless you want the files automatically to be written. For example if you are storing the files in an external source such as a document database (my build talk) then you can just access the stream off the HttpContent instances that are returned from the HttpContent.ReadAsMultiPartAsync method.

Hope this helps

Dec 2, 2011 at 4:41 PM

Just got this method working using Preview 5, to save a posted file...
If anyone notices any issues with the below, or has a better way, please do reply.

 

        [WebInvoke(UriTemplate = "", Method = "POST")]
        public string Upload(HttpRequestMessage request)
        {

            if (request.Content.IsMimeMultipartContent())
            {
                IEnumerable<HttpContent> parts = request.Content.ReadAsMultipart();
                foreach (var content in parts)
                {
                    if (content.Headers.ContentDisposition.Name == "\"file\"")
                    {
                        FileStream fs = File.Create("c:\\123\\some.jpg");
                        byte[] bytesInStream = content.ReadAsByteArray();
                        fs.Write(bytesInStream, 0, bytesInStream.Length);
                    }
                }
            }
            return "done";
        }

Coordinator
Dec 2, 2011 at 8:22 PM

Take a look at the MimeMultipart sample to see how you can do this using one of the out of box stream providers:

http://wcf.codeplex.com/SourceControl/changeset/view/66aa503c963c#WCFWebApi%2fsamples%2fadvanced%2fMimeMultipart.Sample%2fServices%2fFileService.cs

Daniel Roth