XCool
XCool

Reputation: 996

Get filename from Content-Disposition header

I'm currently checking for this header and if it's available, I'll try to get the filename from it. Question is, what is the best method to retrieve it? I understand that Content-Disposition header may appear with different parameters. Examples below:

Content-Disposition = "inline; filename=sample-file-123.pdf"
Content-Disposition = "attachment; filename="123.zip""

I'm only interested to get the filename.

Upvotes: 3

Views: 3704

Answers (3)

de.
de.

Reputation: 8507

There is a dedicated API for this: URLResponse.suggestedFilename

So if you are getting your header from a URLResponse you just call

let filename: String = response.suggestedFilename ?? "default"

and you're done. Note that despite what the documentation says, the return value is optional so you have to provide a default or force unwrap if you dare (I wouldn't).

From the documentation:

The method first checks if the server has specified a filename using the content disposition header. If no valid filename is specified using that mechanism, this method checks the last path component of the URL. If no valid filename can be obtained using the last path component, this method uses the URL's host as the filename. If the URL's host can't be converted to a valid filename, the filename "unknown" is used. In mose cases, this method appends the proper file extension based on the MIME type. This method always returns a valid filename.

Upvotes: 8

Rose Perrone
Rose Perrone

Reputation: 63566

+ (NSString *)filenameFromContentDispositionHeader:(NSString *)contentDispositionHeader {
  NSString *pattern = @"filename=\"(.*)\"";
  NSRegularExpression *regex =
      [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:nil];
  NSTextCheckingResult *result =
      [regex firstMatchInString:contentDispositionHeader
                        options:0
                          range:NSMakeRange(0, contentDispositionHeader.length)];
  NSRange resultRange = [result rangeAtIndex:0];

  if (resultRange.location == NSNotFound) {
    return nil;
  } else {
    return [contentDispositionHeader substringWithRange:
        NSMakeRange(resultRange.location + 10, resultRange.length - 11)];
  }
}

Note that you'll need to modify the pattern if you can't be sure the filename is surrounded in double-quotes.

Upvotes: 2

Manuel
Manuel

Reputation: 10303

I would do something along the lines of this:

- (NSString *)getFilenameFrom:(NSString *)string {
    NSRange startRange = [string rangeOfString:@"filename="];

    if (startRange.location != NSNotFound && startRange.length != NSNotFound) {
        int filenameStart = startRange.location + startRange.length;
        NSRange endRange = [string rangeOfString:@" " options:NSLiteralSearch range:NSMakeRange(filenameStart, [string length] - filenameStart)];
        int filenameLength = 0;

        if (endRange.location != NSNotFound && endRange.length != NSNotFound) {
            filenameLength = endRange.location - filenameStart;
        } else {
            filenameLength = [string length] - filenameStart;
        }

        return [string substringWithRange:NSMakeRange(filenameStart, filenameLength)];
    }
    return nil; //or return @"", whatever you like
}

You will have to check it as i made this in the browser (dont have access to xcode atm).

Upvotes: 2

Related Questions