0

I have an MVC web api that takes in an attachment on a form and it also requires a text and integer field for the upload. The fields are a (string)userId to verify the uploader and a (int)requestId to connect the attachment to the request in the database. Currently it works but with the multipart form data I need to take the fields from the request rather than as parameters in the form submission. My problem is that when the file is saved I need to modify the name of the file with the requestId (from the form) and the attachment Id (from the database) so it reads (requestId)-(attachmentId)-(fileName). The method I have been using involves downloading the file, then getting the data from the request because before the provider is initialized and the file downloaded it always returns null, then renaming the file both in the database and on the file system. I was hoping someone might have a better way of doing this so I can do the renaming with the proper variables in one step.

Here is what I have right now for my controller

public async Task<HttpResponseMessage> PostFormData()
    {
        const string folderName = "uploads";
        if (!Request.Content.IsMimeMultipartContent())
        {
            throw new HttpResponseException((HttpStatusCode.UnsupportedMediaType));
        }
        var rootUrl = Request.RequestUri.AbsoluteUri.Replace(Request.RequestUri.AbsolutePath, String.Empty);
        // this is needed to create an attachment and get the attachment id for the file
        var value = new Attachment {UserId = "1", RequestId = 1};
        value = _dataManager.CreateNewAttachment(value);

        string root = HttpContext.Current.Server.MapPath("~/" + folderName);
        // this is where the provider gets initialized and also where the file is actually downloaded
        // in here I have it set up to rename the file but I can't seem to get the parameters needed for the file name before hand
        var provider = new CustomMultipartFormDataStreamProvider(root, value.CreditRequestId, value.Id);

        try
        {
            // this is the command that allows me to get the data from the inputs on the form, 
            // but it requires the initialized provider which downloaded the file to get it
            await Request.Content.ReadAsMultipartAsync(provider);
            // and here is wher I actually get the input parameters I needed
            foreach (var key in provider.FormData.AllKeys)
            {
                foreach (var val in provider.FormData.GetValues(key))
                {
                    //Trace.WriteLine(string.Format("{0}: {1}", key, val));
                    if (key == "requestId")
                    {
                        value.CreditRequestId = int.Parse(val);
                    }
                    if (key == "userId")
                    {
                        value.UserId = val;
                    }
                }
            }
            // below here is where I have it checking security against the userId, destroying the file if it is determined to be unsafe
            // and where I rename the file as well as modify the database with the proper content for the attachment

Below is my Custom multipart form data stream provider that I initialized to get the file and change the name but before this I am unable to get the parameters I need to do this properly without having to revisit the file and rename it

public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
    public int RequestId;
    public int AttachmentId;

    public CustomMultipartFormDataStreamProvider(string path, int requestId, int attachmentId)
        : base(path)
    {
        RequestId = requestId;
        AttachmentId = attachmentId;
    }

    public override string GetLocalFileName(HttpContentHeaders headers)
    {
        //int requestId = 0;
        //int attachmentId = 0;
        var name = !string.IsNullOrEmpty(headers.ContentDisposition.FileName)
                       ? RequestId + "-" + AttachmentId + "-" + headers.ContentDisposition.FileName
                       : RequestId + "-" + AttachmentId + "-NoName";
        return name.Replace("\"", string.Empty);
    }

I have searched google and SO looking for an answer to this and I thought I had a few answers but they didn't seem to work, I have tried getting the data from within the provider but it always returns null. Please if anyone has a solution to this it would be greatly appreciated

4

1 回答 1

0

So I managed to figure it out, in case anyone else is looking for the answer themselves I queried the httpContext prior to creating the file, with the web api it is still able to read the form data sent from the UI

 public async Task<HttpResponseMessage> PostFormData()
    {
        const string folderName = "uploads";
        if (!Request.Content.IsMimeMultipartContent())
        {
            throw new HttpResponseException((HttpStatusCode.UnsupportedMediaType));
        }
        var rootUrl = Request.RequestUri.AbsoluteUri.Replace(Request.RequestUri.AbsolutePath, String.Empty);

        // this is where I get the form information now prior to actually recieving the file
        int requestId = int.Parse(HttpContext.Current.Request.Form["requestId"]);
        string userId = HttpContext.Current.Request.Form["userId"];


        var value = new Attachment {UserId = userId, RequestId = requestId};
        value = _dataManager.CreateNewAttachment(value);


        string root = HttpContext.Current.Server.MapPath("~/" + folderName);

        var provider = new CustomMultipartFormDataStreamProvider(root, value.RequestId, value.Id);

        try
        {

            await Request.Content.ReadAsMultipartAsync(provider);

            var httpRequest = HttpContext.Current.Request;

            foreach (string file in httpRequest.Files)
            {
            var postedFile = httpRequest.Files[file];
            var filePath = rootUrl + "/" + folderName + "/";

            value.Path = filePath + value.RequestId + "-" + value.Id + "-" + postedFile.FileName;
            value.FileName = value.RequestId + "-" + value.Id + "-" + postedFile.FileName;

            }

after all that then I can include my business logic to determine if the file is safe and I don't have to call a move function on the file to rename it on the server. it makes it a bit more stable as well.

于 2013-09-03T16:43:10.893 回答