NSURLSession backgroundSession uploadTaskWithRequest:fromFile does not work properly, ignores request Content-type

Hi everyone!

I'm using a backgroundSessionConfiguration and was reading the file to a NSData, creating the request manually and sending the data using:

self.uploadDataTask = [self.sessionURLBackground uploadTaskWithStreamedRequest:request];


I remember that it was the only way I made it to work. But it leads to memory leak and loss connection the the background service... Now I'm trying to use:

self.uploadDataTask = [self.sessionURLBackground uploadTaskWithRequest:request fromFile:[[NSURL alloc] initFileURLWithPath:self.pathFile]];


This should be the correct way and maybe get rid of the memory leaking problem... BUT... now I cant get my uploads to work... even if I set:

[request addValue:@"image/jpeg" forHTTPHeaderField:@"Content-Type"];

When I debug the request it ignores my Content-Type and it send this POST request to the PHP server:

{

args = {

};

data = "data:application/octet-stream;base64,/9j/4AAQSkZJRgABAQAASABIAA ...";

files = {

};

form = {

};

headers = {

Accept = "*/";

"Accept-Encoding" = "gzip, deflate";

"Accept-Language" = "pt-br";

Appagent = iOS;

"Content-Length" = 510900;

"Content-Type" = "image/jpeg";

Host = "httpbin.org";

Idsession = "xxxxxxxxxxxx-xxxx-xxxxx-xxxxxxx";

"User-Agent" = "SFTest/1 CFNetwork/758.1.6 Darwin/15.0.0";

};

json = "<null>";

origin = "xxx.xxx.xxx.xxx";

url = "http:/

}


It ignores my request Content-Type and sends in the "data" field as application/octet-stream instead of "files" field... and the PHP says that $_FILES is empty...


I tried image/jpeg and multipart/form-data, the image/jpeg is the one I posted and the multipart/form-data returns a server 500 Error from httpbin.org/post

Answered by DTS Engineer in 84510022

You’re speaking PHP but NSURLSession speaks HTTP. In HTTP there is:

  • a request method

  • a set of headers

  • a request body

The “Content-Type” is an HTTP header. It is meant to describe the format of the data in the request body but NSURLSession does not enforce that. Nor does it provide any infrastructure for setting the request body to any of the various specialised formats out there.

I suspect that your PHP code is expecting the data to be formatted as “multipart/form-data” (see RFC 2388). If that’s the case then you’ll either have to change your client code to put the data in that format or change your server code to not require that format.

A lot of the time I use PUT rather than POST because servers generally make fewer implicit assumptions about the content of a PUT request.

IMPORTANT “multipart/form-data” is a common format for a POST request body but it’s not the only format in common use. Moreover, there’s no HTTP-level requirement as to the format of a POST request body, which is why NSURLSession isn’t able to do this work for you.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

You wrote:

It ignores my request Content-Type and sends in the "data" field as application/octet-stream instead of "files" field...

Huh? The Content-Type value you set is clearly visible in the headers your printed:

headers =    {
    …
    "Content-Type" = "image/jpeg";
    …
};

My guess is that this is an issue on the server side rather than an issue with what the client sent. You can confirm that by looking at a packet trace or a CFNetwork diagnostics log.

ps I moved your thread to Core OS / Networking.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi eskimo,


with this line:

It ignores my request Content-Type and sends in the "data" field as application/octet-stream instead of "files" field...

I should explain myself better. The website httpbin.org/post prints the entire POST request it gets. When I make a POST request the image data is inserted in the "data" field of the POST request and not in the "files" field. So PHP says that $_FILES is empty as you can see in the response of the httpbin.org website:

...

data = "data:application/octet-stream;base64,/9j/4AAQSkZJRgABAQAASABIAA ... lots on lines of image data...";

files = {

};

...

and the Resquest Header is set as "Content-Type" = "image/jpeg"; because I set it with the line [request addValue:@"image/jpeg" forHTTPHeaderField:@"Content-Type"];


even so, when I:

self.uploadDataTask = [self.sessionURLBackground uploadTaskWithRequest:request fromFile:[[NSURL alloc] initFileURLWithPath:self.pathFile]];

the file data is added to "data" field of the POST request body as a application/octet-stream;base64 and not in the "files" field where PHP will parse to $_FILES.


What I'm saying is how can I set the Content-Type for uploadTaskWithRequest:fromFile so it adds not as a application/octet-stream in "data" and adds as a image/jpeg in the "files" field? Or in other words, how can I send a file so that a PHP server recognizes it in $_FILES?

Accepted Answer

You’re speaking PHP but NSURLSession speaks HTTP. In HTTP there is:

  • a request method

  • a set of headers

  • a request body

The “Content-Type” is an HTTP header. It is meant to describe the format of the data in the request body but NSURLSession does not enforce that. Nor does it provide any infrastructure for setting the request body to any of the various specialised formats out there.

I suspect that your PHP code is expecting the data to be formatted as “multipart/form-data” (see RFC 2388). If that’s the case then you’ll either have to change your client code to put the data in that format or change your server code to not require that format.

A lot of the time I use PUT rather than POST because servers generally make fewer implicit assumptions about the content of a PUT request.

IMPORTANT “multipart/form-data” is a common format for a POST request body but it’s not the only format in common use. Moreover, there’s no HTTP-level requirement as to the format of a POST request body, which is why NSURLSession isn’t able to do this work for you.

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you eskimo!


I feared that changing the PHP code to support this POST request was the only option. I was trying to avoid that because we also have an app for Windows Phone and Android which is working fine with multipart/form-data.


Thanks, I'll change the server PHP side to support this kind of request and I'll let you know if it fixed the memory leak problem.

I've changed the PHP to accept this new type of request and it works just fine 🙂 and without any memory leak!!!


Wooohoooooo!!!


Thanks 🙂

NSURLSession backgroundSession uploadTaskWithRequest:fromFile does not work properly, ignores request Content-type
 
 
Q