Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
741 views
in Technique[技术] by (71.8m points)

objective c - NSURLRequest Upload Multiple Files

I'm trying to upload multiple files to a server using a POST request, but for some reason, only one file is submitted. I suspect I'm doing something wrong with the boundary, but I'm not sure where...

What's wrong with my code?

    [_serverRequest setHTTPMethod:@"POST"];

    NSString *_boundary = @"14737809831466499882746641449";

    NSString *_contentType = [NSString stringWithFormat:@"multipart/form-data; charset=UTF-8; boundary=%@",_boundary];
    [_serverRequest setValue:_contentType forHTTPHeaderField:@"Content-Type"];

    /* reqeuest body */
    NSMutableData *_requestBody = [NSMutableData data];

    for (id _instance in self.currentBrowserInstances)
    {
        if ([_instance respondsToSelector:@selector(pathToDatabase)])
        {
            NSString *_databasePath = [_instance pathToDatabase];

            NSMutableString *_filename = [NSMutableString stringWithString:[self _generatedFilename]];
            [_filename appendFormat:@"_%@", [[_instance name] stringByReplacingOccurrencesOfString:@" " withString:@""]];

            if (_databasePath.pathExtension.length > 0)
                [_filename appendFormat:@".%@", _databasePath.pathExtension];

            /* Build Request Body */
            [_requestBody appendData:[[NSString stringWithFormat:@"--%@
", _boundary] dataUsingEncoding:NSUTF8StringEncoding]];
            [_requestBody appendData:[[NSString stringWithFormat:@"Content-Disposition: multipart/form-data; name="databases"; filename="%@"
", _filename] dataUsingEncoding:NSUTF8StringEncoding]];
            [_requestBody appendData:[@"Content-Type: application/octet-stream

" dataUsingEncoding:NSUTF8StringEncoding]];
            [_requestBody appendData:[NSData dataWithContentsOfFile:_databasePath]];
            [_requestBody appendData:[@"
" dataUsingEncoding:NSUTF8StringEncoding]];
        }
    }

    [_requestBody appendData:[[NSString stringWithFormat:@"--%@--
", _boundary] dataUsingEncoding:NSUTF8StringEncoding]];

    [_serverRequest setHTTPBody:_requestBody];

Here's the server response:

Array
(
    [databases] => Array
        (
            [name] => QTSKUFJM_test.dat
            [type] => application/octet-stream
            [tmp_name] => /private/var/tmp/php3w7qd5
            [error] => 0
            [size] => 64576
        )
)
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The final delimiter (the "encapsulation boundary") following the last multipart, is the same as for a body parts plus two hyphens.

Thus, the final delimiter looks as follows:

--<boundary>--

and not

--<boundary>--<boundary>--

as in your implementation ;)

A CRLF following the last delimiter won't hurt, also.


Edit:

A more extensive answer

The corresponding specification of a multipart/form-data is "Form-based File Upload in HTML" RFC 1867 and RFC 2388.

The multipart media type is defined more precisely in RFC 2046 (which obsoletes RFC 1521, RFC 1522, RFC 1590).

The following are relevant excerpts from RFC 2388 and RFC 2046:

According RFC 2388, a multipart/form-data contains a series of parts. Each part must contain a particular content-disposition header (see RFC 2183) where the disposition type is "form-data" and where the disposition has an additional parameter of "name". For example:

Content-Disposition: form-data; name="user"

Note: The most recent RFC for content-disposition header is defined in RFC 2231 (Updates: 2045, 2047, 2183; Obsoletes RFC 2184)).

The media-type multipart/form-data strictly follows the rules of all multipart MIME data streams:

Subtypes of the "multipart" type must use an identical syntax. Subtypes may differ in their semantics, and may impose additional restrictions on syntax, but must conform to the required syntax for the "multipart" type.

Basically, the body of a multipart must contain one or more body parts, each preceded by a boundary delimiter line, and the last one followed by a closing boundary delimiter line.

Each body part consists of a header area, a blank line, and a body area. The header area is allowed to be empty.

Note: a body part is an entity, and not a RFC 882 message. That is, no headers are actually required in body parts. The absence of a Content-Type header usually indicates that the corresponding body has a content-type of "text/plain; charset=US-ASCII".

If the contents of a file is to be transfered, then "Content-Type" shall be set to the file's media type, if known, otherwise "application/octet-stream".

Hint: The only header fields that have defined meaning for body parts are those whose names begin with "Content-". All other header fields may be ignored in body parts.

Note: If multiple files are to be returned as the result of a single form entry, they should be represented as a multipart/mixed part embedded within the multipart/form-data.

The original local file name may be supplied as well, either as a "filename" parameter either of the "content-disposition: form-data" header or, in the case of multiple files, in a "content-disposition: file" header of the subpart.

Common Syntax

The common syntax for the multipart media type is defined in RFC 2046 § 5.1.1

Here is a simplified more comprehensive form:

A multipart must have a Content-Type. For example:

 Content-Type: multipart/subtype; boundary=gc0p4Jq0M2Yt08j34c0p

For the boundary there are certain limitations (please see RFC 2046). In practice, enclosing it in double quotes makes it more robust:

 Content-Type: multipart/subtype; boundary="---- boundary which requires quotes -----"

Each part is preceded by a boundary delimiter. The boundary delimiter MUST occur at the beginning of a line (that is, following a CRLF). Conceptually, the CRLF belongs to the boundary, rather to the preceding element:

boundary-delimiter :=     CRLF "--" boundary 

Note: the boundary may be followed by zero or more whitespace, which is not shown in this BNF.

A multipart body consists of one or more encapsulations followed by a closing delimiter:

multipart-body :=         +encapsulation
                          end-boundary-delimiter

where an encapsulation is a boundary delimiter followed by a CRLF followed by the body part:

encapsulation :=          boundary-delimiter CRLF 
                          body-part
                          
                          

The body part (the entity) consists of entity-headers and the body:

body-part :=              MIME-part-headers [CRLF *OCTET]

Note: An entity-header (a MIME-part-header) will be delimited by a CRLF - as any other header.

The last part is followed by a closing delimiter:

end-boundary-delimiter := CRLF "--" boundary "--"

Caution:

Despite the massive amount of papers, the above definition for boundaries is still unclear - if not ambiguous!

In RFC 2046, § 5.1.1 Common Syntax, it states:

It (the boundary) is then terminated by either another CRLF and the header fields for the next part, or by two CRLFs, in which case there are no header fields for the next part.

and

The CRLF preceding the boundary delimiter line is conceptually attached to the boundary...

A body-part may be completely empty (according the BNF). A boundary will be followed by one CRLF and then followed by either a boundary-delimiter or a end-boundary-delimiter. Only since the next delimiter starts itself with a CRLF, the preceding boundary has two subsequent CRLFs!


Examples

Note, all CR and LF are shown explicitly.

Example: multipart-body with one part, two entity headers:

boundary = "1234567890"


--1234567890

header1: value1

header2: value2


<data>

--0123456789--

Example: multipart-body with two parts:


--1234567890

header1: value1

header2: value2


<data1>

--0123456789

header1: value1


<data2>

--0123456789--

Example: multipart-body with no headers:


--1234567890


<data1>

--0123456789--

Example: multipart-body with empty body-part:


--1234567890


--0123456789--

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...