web-dev-qa-db-fra.com

Méthode WebAPI qui prend un téléchargement de fichier et des arguments supplémentaires

Je veux télécharger un fichier et envoyer avec le fichier des informations supplémentaires, disons une chaîne foo et une barre int.

Comment pourrais-je écrire une méthode de contrôleur ASP.NET WebAPI qui reçoit un téléchargement de fichier, une chaîne et un int?

Mon JavaScript:

var fileInput = document.querySelector("#filePicker");
var formData = new FormData();
formData.append("file", fileInput.files[0]);
formData.append("foo", "hello world!");
formData.append("bar", 42);

var options = {
   url: "/api/foo/upload",
   data: formData,
   processData: false // Prevents JQuery from transforming the data into a query string
};
$.ajax(options);

Mon contrôleur WebAPI peut accéder au fichier comme ceci:

public async Task<HttpResponseMessage> Upload()
{
    var streamProvider = new MultipartMemoryStreamProvider();
    await Request.Content.ReadAsMultipartAsync(streamProvider);
    var fileStream = await streamProvider.Contents[0].ReadAsStreamAsync();
}

Mais je ne sais pas comment je peux obtenir ma chaîne et mon int. Je pense que je peux probablement dire streamProvider.Content [1], ou autre chose, mais cela semble super méchant.

Quelle est la bonne façon © d'écrire une action WebAPI qui accepte un téléchargement de fichier, une chaîne et un int?

17

Vous pouvez créer votre propre MultipartFileStreamProvider pour accéder aux arguments supplémentaires.

Dans ExecutePostProcessingAsync, nous parcourons chaque fichier en plusieurs parties et chargeons les données personnalisées (si vous n'avez qu'un seul fichier, vous n'aurez qu'un seul objet dans la liste CustomData).

class MyCustomData
{
    public int Foo { get; set; }
    public string Bar { get; set; }
}

class CustomMultipartFileStreamProvider : MultipartMemoryStreamProvider
{
    public List<MyCustomData> CustomData { get; set; }

    public CustomMultipartFileStreamProvider()
    {
        CustomData = new List<MyCustomData>();
    }

    public override Task ExecutePostProcessingAsync()
    {
        foreach (var file in Contents)
        {
            var parameters = file.Headers.ContentDisposition.Parameters;
            var data = new MyCustomData
            {
                Foo = int.Parse(GetNameHeaderValue(parameters, "Foo")),
                Bar = GetNameHeaderValue(parameters, "Bar"),
            };

            CustomData.Add(data);
        }

        return base.ExecutePostProcessingAsync();
    }

    private static string GetNameHeaderValue(ICollection<NameValueHeaderValue> headerValues, string name)
    {
        var nameValueHeader = headerValues.FirstOrDefault(
            x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase));

        return nameValueHeader != null ? nameValueHeader.Value : null;
    }
}

Puis dans votre contrôleur:

class UploadController : ApiController
{
    public async Task<HttpResponseMessage> Upload()
    {
        var streamProvider = new CustomMultipartFileStreamProvider();
        await Request.Content.ReadAsMultipartAsync(streamProvider);

        var fileStream = await streamProvider.Contents[0].ReadAsStreamAsync();
        var customData = streamProvider.CustomData;

        return Request.CreateResponse(HttpStatusCode.Created);
    }
}
19
Ben Foster

Je pense que les réponses ici sont excellentes. Ainsi, d'autres peuvent voir un exemple assez simple de la façon de transmettre des données en plus du fichier sous forme résumée, une fonction Javascript est incluse qui effectue l'appel WebAPI au contrôleur FileUpload et l'extrait de code du contrôleur FileUpload (dans VB.net) qui lit les données supplémentaires transmises par Javascript.

Javascript:

            function uploadImage(files) {
            var data = new FormData();
            if (files.length > 0) {
                data.append("UploadedImage", files[0]);
                data.append("Source", "1")
                var ajaxRequest = $.ajax({
                    type: "POST",
                    url: "/api/fileupload/uploadfile",
                    contentType: false,
                    processData: false,
                    data: data
                });

Contrôleur de téléchargement de fichiers:

        <HttpPost> _
    Public Function UploadFile() As KeyValuePair(Of Boolean, String)
        Try
            If HttpContext.Current.Request.Files.AllKeys.Any() Then
                Dim httpPostedFile = HttpContext.Current.Request.Files("UploadedImage")
                Dim source = HttpContext.Current.Request.Form("Source").ToString()

Ainsi, comme vous pouvez le voir dans le Javascript, les données supplémentaires transmises sont la clé "Source" et la valeur est "1". Et comme Chandrika a répondu ci-dessus, le contrôleur lit ces données transmises via "System.Web.HttpContext.Current.Request.Form (" Source "). ToString ()".

Notez que Form ("Source") utilise () (vs. []) car le code du contrôleur est dans VB.net.

J'espère que cela t'aides.

5
Sam

Vous pouvez extraire plusieurs fichiers et plusieurs attributs de cette manière:

public async Task<HttpResponseMessage> Post()
{
    Dictionary<string,string> attributes = new Dictionary<string, string>();
    Dictionary<string, byte[]> files = new Dictionary<string, byte[]>();

    var provider = new MultipartMemoryStreamProvider();
    await Request.Content.ReadAsMultipartAsync(provider);
    foreach (var file in provider.Contents)
    {
        if (file.Headers.ContentDisposition.FileName != null)
        {
            var filename = file.Headers.ContentDisposition.FileName.Trim('\"');
            var buffer = await file.ReadAsByteArrayAsync();
            files.Add(filename, buffer);
        } else
        {
            foreach(NameValueHeaderValue p in file.Headers.ContentDisposition.Parameters)
            {
                string name = p.Value;
                if (name.StartsWith("\"") && name.EndsWith("\"")) name = name.Substring(1, name.Length - 2);
                string value = await file.ReadAsStringAsync();
                attributes.Add(name, value);
            }
        }
    }
    //Your code here  
    return new HttpResponseMessage(HttpStatusCode.OK);
}
3
Renzo Ciot

Vous pouvez le faire de la manière suivante: Méthode JQuery:

    var data = new FormData();

    data.append("file", filesToUpload[0].rawFile);
    var doc = {};            
    doc.DocumentId = 0; 
    $.support.cors = true;
    $.ajax({
        url: '/api/document/uploaddocument',
        type: 'POST',
        contentType: 'multipart/form-data',
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        success: function (response) {
            docId = response.split('|')[0];
            doc.DocumentId = docId;
            $.post('/api/document/metadata', doc)
                .done(function (response) {
                });
          alert('Document save successfully!');
        },
        error: function (e) {
            alert(e);
        }
    });

appelez votre API Web "UploadDocuement"

[Route("api/document/uploaddocument"), HttpPost]
        [UnhandledExceptionFilter]
        [ActionName("UploadDocument")]
        public Task<HttpResponseMessage> UploadDocument()
        {
            // Check if the request contains multipart/form-data.
            if (!Request.Content.IsMimeMultipartContent())
            {
                Task<HttpResponseMessage> mytask = new Task<HttpResponseMessage>(delegate()
                {
                    return new HttpResponseMessage()
                    {
                        StatusCode = HttpStatusCode.BadRequest,
                        Content = "In valid file & request content type!".ToStringContent()
                    };
                });
                return mytask;
            }


            string root = HttpContext.Current.Server.MapPath("~/Documents");
            if (System.IO.Directory.Exists(root))
            {
                System.IO.Directory.CreateDirectory(root);
            }
            var provider = new MultipartFormDataStreamProvider(root);

            var task = Request.Content.ReadAsMultipartAsync(provider).
            ContinueWith<HttpResponseMessage>(o =>
            {
                if (o.IsFaulted || o.IsCanceled)
                    throw new HttpResponseException(HttpStatusCode.InternalServerError);

                FileInfo finfo = new FileInfo(provider.FileData.First().LocalFileName);

                string guid = Guid.NewGuid().ToString();

                File.Move(finfo.FullName, Path.Combine(root, guid + "_" + provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "")));

                string sFileName = provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "");

                FileInfo FInfos = new FileInfo(Path.Combine(root, guid + "_" + provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "")));

                Document dbDoc = new Document()
                {
                    DocumentID = 0                

                };

                context.DocumentRepository.Insert(dbDoc);
                context.Save();

                return new HttpResponseMessage()
                {
                    Content = new StringContent(string.Format("{0}|File uploaded.", dbDoc.DocumentID))
                };
            }
           );
            return task;

        }

Appelez votre API Web de métadonnées de la manière suivante:

[Route("api/document/metadata"), HttpPost]
        [ActionName("Metadata")]
        public Task<HttpResponseMessage> Metadata(Document doc)
        {
            int DocId = Convert.ToInt32(System.Web.HttpContext.Current.Request.Form["DocumentId"].ToString());

            Task<HttpResponseMessage> mytask = new Task<HttpResponseMessage>(delegate()
            {
                return new HttpResponseMessage()
                {
                    Content = new StringContent("metadata updated")
                };
            });
            return mytask;
        }
1