如何从图像和文件发送多部分表单数据,从ios到php服务器?

I tried to send form data (with image and file uploads) from ios to php server, failed in all attempts. After debugging, I found that:

  • When I send only form data and image, it works fine. But when I include file upload (in this case, an mp3 file), it failed. The form data and the image file are not even delivered to the php side.
  • If I do not include the mp3 file, the server side will get the form data and image file.
  • If I send only the mp3 file, it does not work.

This is my ios code:

//beginning data
NSDictionary *dictSong;
NSArray *arrImages; // Array of UIImage
NSArray *arrFiles; // Array of local file names to be sent to the server

// Build the request body
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:link]];
[request setHTTPMethod:@"POST"];

NSMutableData *body = [NSMutableData data];

NSString *boundary = @"---------------------------14737809831466499882746641449";
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
[request addValue:contentType forHTTPHeaderField:@"Content-Type"];

// add the text form fields
if([dictData count] > 0) {
    for (id key in dictData) {
        [body appendData:[[NSString stringWithFormat:@"--%@
", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"

", key] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithString:[dictData valueForKey:key]] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[@"
" dataUsingEncoding:NSUTF8StringEncoding]];
    }
}
// add the image form fields
if ([arrImages count] > 0) {
    for(id key in arrImages) {
        [body appendData:[[NSString stringWithFormat:@"--%@
", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"Content-Disposition: attachment; name=\"picture\"; filename=\"image.png\"
"] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[@"Content-Type: image/png

" dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[NSData dataWithData:UIImageJPEGRepresentation(key, 1.0)]];
        //[body appendData:[NSData dataWithData:key]];
        [body appendData:[@"
" dataUsingEncoding:NSUTF8StringEncoding]];
    }
}
// add the file form fields
if ([arrFiles count] != 0) {
    for(id key in arrFiles) {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *docsDir = [paths objectAtIndex:0];
        NSString *dataPath = docsDir;
        if(folder)
            dataPath = [docsDir stringByAppendingPathComponent:[NSString stringWithFormat:@"/%@",folder]];
        NSString *fullPath = [dataPath stringByAppendingFormat:@"/%@", key];
        NSLog(@"fullPath: %@", fullPath); // file path has also been checked

        [body appendData:[[NSString stringWithFormat:@"--%@
", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[[NSString stringWithFormat:@"Content-Disposition: attachment; name=\"file\"; filename=\"file.mp3\"
"] dataUsingEncoding:NSUTF8StringEncoding]];
        //[body appendData:[@"Content-Type: application/octet-stream

" dataUsingEncoding:NSUTF8StringEncoding]]; // I tried this line and the following line, but still failed
        [body appendData:[@"Content-Type: audio/mp3

" dataUsingEncoding:NSUTF8StringEncoding]];
        [body appendData:[NSData dataWithContentsOfFile:fullPath]];
        [body appendData:[@"
" dataUsingEncoding:NSUTF8StringEncoding]];
    }
}

// close the form
[body appendData:[[NSString stringWithFormat:@"--%@--
", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

// set request body
[request setHTTPBody:body];

And then I send the request through NSURLSESSION:

NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration];

NSURLSessionDataTask *uploadTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { //Treat the response
}];

At the server side, I check:

echo $_POST["username"];
echo $_POST["password"];

or

$_FILES["picture"]["size"]

They are nil if I include arrFiles to the form submit, but correct data otherwise. What did I do wrong?

I did not succeed in sending form data alongside file upload. So the way around was that:

  1. I post a form data alongside image file in a data task to the server;
  2. After inserting the data in the database, I instruct the server to return the id of the newly inserted item back to the app;
  3. I upload the song file through upload task to the server, using the id received in point 2 above.

Cheers! For those who may have a better solution, please share. Thanks.