1

I have following ASP.net WebAPI code which handles file uploads. Works great using a simple HTML file upload form.

public Task<IEnumerable<string>> UploadFile()
{
  if (Request.Content.IsMimeMultipartContent())
  {

      string fullPath = HttpContext.Current.Server.MapPath("~/uploads");

      MultipartFormDataStreamProvider streamProvider = new     MultipartFormDataStreamProvider(fullPath);

      var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith(t =>
      {
         if (t.IsFaulted || t.IsCanceled)
         {
            throw new HttpResponseException(HttpStatusCode.InternalServerError);
          }

          var fileInfo = streamProvider.FileData.Select(i =>
          {
               var info = new FileInfo(i.LocalFileName);
                  return "File uploaded as " + info.FullName + " (" + info.Length + ")";
                    });
                    return fileInfo;

                });
                return task;
          }
          else
          {
     HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "Invalid Request!"));
              return null;
          }
    }

But gives "Unexpected end of MIME multipart stream MIME multipart message is not complete" if called from Objective C code, this I figured out through tracing on the API side. Following is the Objective C method...

- (void) uploadFile
{
NSString *fileUploadSrvURL = @"http://server1/service/api/controller/uploadfile";

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:fileUploadSrvURL]];
[request setHTTPMethod:@"POST"];
NSString *boundary = @"---------------------------14737809831466499882746641449";
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary];
[request setValue:contentType forHTTPHeaderField:@"content-type"];

NSURL *fullFileURL = [self getFilePath:CurrentVisitId];
NSData *fileData = [NSData dataWithContentsOfURL:fullFileURL];

NSMutableData *body = [NSMutableData data];

[body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"FileName\"; filename=\"%@\"\r\n",@"810474.rtf"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithString:@"Content-Type: application/rtf\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:fileData];
[body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:body];

NSHTTPURLResponse* response =[[NSHTTPURLResponse alloc] init];
NSError* error = [[NSError alloc] init] ;

NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (error)
{

}

NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(@"%@", responseString);

}

As the ASP.net Web API side code works fine if integrated with HTML Form upload, so, probabaly there is something wrong with the way I am calling it from Objective C (I am new to Objective C)

2 Answers 2

1

I had similar issue and I manage to sorted it by creating a helper class (I found it online but I don't remember where):

public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
    public CustomMultipartFormDataStreamProvider(string path)
        : base(path)
    { }

    public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
    {
        var name = !string.IsNullOrWhiteSpace(headers.ContentDisposition.FileName) ? headers.ContentDisposition.FileName : "NoName";
        return name.Replace("\"", string.Empty); //this is here because Chrome submits files in quotation marks which get treated as part of the filename and get escaped
    }
}

And you also needs to change couple of line in your code:

if (Request.Content.IsMimeMultipartContent())
        {
            string fullPath = HttpContext.Current.Server.MapPath("~/uploads");
            var streamProvider = new CustomMultipartFormDataStreamProvider(fullPath);
            var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith<IEnumerable<string>>(t =>
            {
                if (t.IsFaulted || t.IsCanceled)
                {
                    throw new HttpResponseException(HttpStatusCode.InternalServerError);
                }
        ///.........
        /// rest of your code
}

The difference is that I call the web api via AFNetworking if it doesn't work for you I can share my AFNetworking code as well.

// EDITED My .h file

#import "AFHTTPClient.h"

typedef void (^DataResponseSuccessBlock)(id JSON);
typedef void (^DataResponseFailureBlock)(NSError *error);

@interface MyHTTPClient : AFHTTPClient


+ (MyHTTPClient *)sharedMyHTTPClient;

- (id)initWithBaseURL:(NSURL *)url;

- (void)saveTemplateData:(TemplateData*)data success:(DataResponseSuccessBlock)successBlock failure:(DataResponseFailureBlock)failureBlock;

@end

My .m file

@implementation GLHTTPClient
+ (MyHTTPClient *)sharedMyHTTPClient
{
    static dispatch_once_t pred;
    static MyHTTPClient *_sharedMyHTTPClient = nil;

    dispatch_once(&pred, ^{ _sharedMyHTTPClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:kJSON_URL]]; });

    return _sharedMyHTTPClient;
}

- (id)initWithBaseURL:(NSURL *)url
{
    self = [super initWithBaseURL:url];
    if (!self)
    {
        return nil;
    }

    [self registerHTTPOperationClass:[AFJSONRequestOperation class]];
    [self setDefaultHeader:@"Accept" value:@"application/json"];
    [self setParameterEncoding:AFJSONParameterEncoding];

    return self;
}
- (void)saveTemplateData:(TemplateData*)data success:(DataResponseSuccessBlock)successBlock failure:(DataResponseFailureBlock)failureBlock
{
    NSData *imageData = UIImagePNGRepresentation(data.image);
    NSMutableURLRequest *request = [self multipartFormRequestWithMethod:@"POST" path:@"SaveData" //My web api class for uploading image
                                                             parameters:nil
                                              constructingBodyWithBlock:^(id <AFMultipartFormData>formData)
                                    {
                                        [formData appendPartWithFileData:imageData
                                                                    name:@"image"
                                                                fileName:@"image.png"
                                                                mimeType:@"image/png"];
                                    }];

    AFHTTPRequestOperation *op = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
        if(successBlock)
            successBlock(responseObject);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        if(failureBlock)
           failureBlock(error);
    }];

    [self enqueueHTTPRequestOperation:op];
}
@end

I have change this code so if something doesn't work for you give me a shout and I can have a look on it again. You need to just call saveTemplateData:success:failure method and it should work.

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for this helper suggestion, but even after integrating with this gives the same error.
Can you add to your code if (info.Exists){// LOG TO SEE HAVE YOU GOT THE FILE } just for debugging?
Thanks Greg, I added that log and it doesn't go inside this block... var fileInfo = streamProvider.FileData.Select(i => {
Can you try add [HttpPost] attribute to your UploadFile method. If it won't work try use AFNetworking or NSURLSession to upload the image to web api.
Thanks for all your help Greg, I am going to try integration with AFNetworking, will it be possible for you to share your code which uses AFNetworking to upload file?
|
0

We messed a lot with this problem on iOS specifically. Did't figured out - why hand made Http multipart request does that error on .Net Server, but went around with a simple AFNetworking (3) usage.

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
[manager.requestSerializer setValue:@"multipart/form-data" forHTTPHeaderField:@"Content-Type"];

[manager.requestSerializer setValue:[[App instance].login token] forHTTPHeaderField:@"Authorization"];

[manager POST:url   parameters:nil
    constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
        for (UIImage *img in [params allValues]) {
            [formData appendPartWithFileData:UIImageJPEGRepresentation(img, 1) name:[NSString stringWithFormat:@"photo_%d",count] fileName:[NSString stringWithFormat:@"photo_%d.jpg",count] mimeType:@"image/jpeg"];
        }
        }
     progress:nil
      success:^(NSURLSessionDataTask *task, id response) {
          NSLog(@"Success");
      } failure:^(NSURLSessionDataTask *task, NSError *error) {
          NSLog(@"Error: %@", error);
      }];

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.