目标C到ASP.net Web API文件上传给出错误MIME多部分流MIME多部分消息的意外结束未完成

时间:2013-11-25 14:53:51

标签: objective-c file-upload asp.net-web-api

我有以下ASP.net WebAPI代码处理文件上传。使用简单的HTML文件上传表单可以很好地工作。

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;
          }
    }

但是如果从Objective C代码调用,则“MIME多部分流MIME多部分消息的意外结束”是完整的,我通过API侧的跟踪得出结论。以下是Objective C方法......

- (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);

}

由于ASP.net Web API端代码与HTML表单上传集成时工作正常,因此,我从Objective C调用它的方式可能有问题(我是Objective C的新手)

2 个答案:

答案 0 :(得分:1)

我有类似的问题,我设法通过创建一个帮助类来对其进行排序(我在网上找到它但我不记得在哪里):

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
    }
}

您还需要在代码中更改几行:

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
}

区别在于我通过AFNetworking调用web api,如果它不适合你,我也可以共享我的AFNetworking代码。

//已编辑 我的.h文件

#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

我的.m文件

@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

我已经更改了这段代码,所以如果有什么东西不起作用,请给我一个喊叫,我可以再看看它。 你需要调用saveTemplateData:success:failure方法,它应该可以工作。

答案 1 :(得分:0)

我们特意在iOS上弄乱了这个问题。没想到 - 为什么手工制作的Http多部分请求在.Net Server上出现了这个错误,但是使用了简单的AFNetworking(3)用法。

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);
      }];