How can I simultaneously pass args and upload a fi

2019-08-07 10:06发布

问题:

I decided that my question here isn't really what I want to do - the XML I need to send is a lot longer, potentially, than I really want to send in a URI.

It didn't "feel" right doing that, and this unsealed the deal.

I need to send both a couple of args AND a file to my Web API app from a client (handheld/CF) app.

I may have found the code for receiving that, from here [ http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2]

Specifically, Wasson's Controller code here looks like it very well might work:

public async Task<HttpResponseMessage> PostFile()
{
    // 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
    {
        StringBuilder sb = new StringBuilder(); // Holds the response body

        // Read the form data and return an async task.
        await Request.Content.ReadAsMultipartAsync(provider);

        // This illustrates how to get the form data.
        foreach (var key in provider.FormData.AllKeys)
        {
            foreach (var val in provider.FormData.GetValues(key))
            {
                sb.Append(string.Format("{0}: {1}\n", key, val));
            }
        }

        // This illustrates how to get the file names for uploaded files.
        foreach (var file in provider.FileData)
        {
            FileInfo fileInfo = new FileInfo(file.LocalFileName);
            sb.Append(string.Format("Uploaded file: {0} ({1} bytes)\n", fileInfo.Name, fileInfo.Length));
        }
        return new HttpResponseMessage()
        {
            Content = new StringContent(sb.ToString())
        };
    }
    catch (System.Exception e)
    {
        return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
    }
}

...but now I need to know how to send it; other calls from the client are of the form:

http://<IPAddress>:<portNum>/api/<ControllerName>?arg1=Bla&arg2=Blee

but how does the file I need to send/attach get passed along? It is a XML file, but I don't want to append the whole thing to the URI, as it can be quite large, and doing so would be horrendously weird.

Does anybody know how to accomplish this?

UPDATE

Following the crumbs tvanfosson dropped below, I found code here which I think I can adapt to work on the client:

var message = new HttpRequestMessage();
var content = new MultipartFormDataContent();

foreach (var file in files)
{
    var filestream = new FileStream(file, FileMode.Open);
    var fileName = System.IO.Path.GetFileName(file);
    content.Add(new StreamContent(filestream), "file", fileName);
}

message.Method = HttpMethod.Post;
message.Content = content;
message.RequestUri = new Uri("http://localhost:3128/api/uploading/");

var client = new HttpClient();
client.SendAsync(message).ContinueWith(task =>
{
    if (task.Result.IsSuccessStatusCode)
    { 
        //do something with response
    }
});    

...but that depends on whether the Compact Framework supports MultipartFormDataContent

UPDATE 2

Which it doesn't, according to How can i determine which .Net features the compact framework has?

UPDATE 3

Using the Bing Search Code for C# extension, I mashed "h", chose "How do I", entered "send file via http" and got this:

WebRequest request = WebRequest.Create("http://www.contoso.com/PostAccepter.aspx ");
request.Method = "POST";
string postData = "This is a test that posts this string to a Web server.";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length;
Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
WebResponse response = request.GetResponse();
Console.WriteLine(((HttpWebResponse)response).StatusDescription);
dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
Console.WriteLine(responseFromServer);
reader.Close();
dataStream.Close();
response.Close();

As I need to add a couple of string args in addition to the file (which I assume I can add via the postData byte array), can I do that by adding more calls to dataStream.Write()? IOW, is this sensible (first and third lines differ):

WebRequest request = WebRequest.Create("http://MachineName:NNNN/api/Bla?str1=Blee&str2=Bloo");
request.Method = "POST";
string postData = //open the HTML file and assign its contents to this, or make it File postData instead of string postData?
// the rest is the same

?

UPDATE 4

Progress: This, such as it is, is working:

Server code:

public string PostArgsAndFile([FromBody] string value, string serialNum, string siteNum)
{
    string s = string.Format("{0}-{1}-{2}", value, serialNum, siteNum);
    return s;
}

Client code (from Darin Dimitrov in this post):

private void ProcessRESTPostFileData(string uri)
{
    using (var client = new WebClient())
    {
        client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
        var data = "=Short test...";
        var result = client.UploadString(uri, "POST", data);
        //try this: var result = client.UploadFile(uri, "bla.txt");
        //var result = client.UploadData()
        MessageBox.Show(result);
    }
}

Now I need to get it sending a file instead of a string in the [FromBody] arg.

回答1:

You should look into using multipart/form-data with a custom media type formatter that will extract both the string properties and the uploaded XML file.

http://lonetechie.com/2012/09/23/web-api-generic-mediatypeformatter-for-file-upload/