Get MIMETYPE by requesting header of the file/web

2019-08-14 09:34发布

问题:

I need to know the MIMEType of the file or web page, available at particular URL and according to MIMEType I’ll take the decision whether to load that page/file to the UIWebView or not. I know, I can get MIMEType with NSURLResponse object, but problem is, when I get this response that page would have already downloaded.

So, is there any way to ask only header of the file/webpage at particular URL, so that I can check the MIMEType and request for that page/file, only when it is required?

回答1:

I see you don't want the page to be loaded in UIWebView until you determine the Content-Type of the response. For doing this you can implement a custom NSURLProtocol. This is what I did, but you are always welcome to enhance or suggest a different solution. Please go through inline comments.

***** CustomURLProtocol.h *****

@interface CustomURLProtocol : NSURLProtocol

@end

***** CustomURLProtocol.m *****

@interface CustomURLProtocol ()

@property(nonatomic, strong) NSURLConnection * connection;
@property(nonatomic, strong) NSMutableData * reponseData;
@property(nonatomic, strong) NSURLRequest * originalRequest;

@end

@implementation CustomURLProtocol

+(BOOL)canInitWithRequest:(NSURLRequest *)request
{

    NSString * urlScheme = [request.URL.scheme lowercaseString];

    //only handling HTTP or HTTPS requests which are not being handled
    return (![[NSURLProtocol propertyForKey:@"isBeingHandled" inRequest:request] boolValue] &&
            ([urlScheme isEqualToString:@"http"] || [urlScheme isEqualToString:@"https"]));
}

+(NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
    return request;
}


-(void)startLoading
{
    NSMutableURLRequest * requestCopy = [self.request mutableCopy];
    [NSURLProtocol setProperty:[NSNumber numberWithBool:YES] forKey:@"isBeingHandled" inRequest:requestCopy];
    self.originalRequest = requestCopy;
    self.connection = [NSURLConnection connectionWithRequest:requestCopy delegate:self];
}

-(void)stopLoading
{
    [self.connection cancel];
}

#pragma mark - NSURLConnectionDelegate methods


-(NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
{

    [self.client URLProtocol:self wasRedirectedToRequest:request redirectResponse:response];

    return request;
}



- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    if ([response isKindOfClass:[NSHTTPURLResponse class]])
    {
        NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
        NSString * contentType = [httpResponse.allHeaderFields valueForKey:@"Content-Type"];

        /*
         Check any header here and make the descision.
         */
        if([contentType containsString:@"application/pdf"])
        {

            /*
                Let's say you don't want to load the PDF in current UIWebView instead you want to open another view controller having a webview for that.
                For doing that we will not inform the client about the response we received from NSURLConnection that we created.
             */

            [self.connection cancel];

            //post a notification carrying the PDF request and load this request anywhere else.
            [[NSNotificationCenter defaultCenter] postNotificationName:@"PDFRequest" object:self.originalRequest];
        }
        else
        {
            /*
                For any other request having Content-Type other than application/pdf,
                we are informing the client (UIWebView or NSURLConnection) and allowing it to proceed.
             */
            [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
        }
    }

}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [self.client URLProtocol:self didLoadData:data];
}


- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{

    [self.client URLProtocolDidFinishLoading:self];
}

- (void)connection:(NSURLConnection *)connectionLocal didFailWithError:(NSError *)error
{
    [self.client URLProtocol:self didFailWithError:error];
}

@end

One more thing which I forgot to mention is that you have to register the CustomURLProtocol in your app delegate like below:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   [NSURLProtocol registerClass:[CustomURLProtocol class]];
    return YES;
}


回答2:

You want to get the headers only. Use HTTP HEAD method for that. Example:

    let request = NSMutableURLRequest(URL: NSURL(string: "your_url")!)
    request.HTTPMethod = "HEAD"
    let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) -> Void in
        // Analyze response here
    }
    task.resume()


回答3:

You can get the content type by using the "Content-Type" property of the response headers:

func getContentType(urlPath: String, completion: (type: String)->()) {
    if let url = NSURL(string: urlPath) {
        let request = NSMutableURLRequest(URL: url)
        request.HTTPMethod = "HEAD"
        let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (_, response, error) in
            if let httpResponse = response as? NSHTTPURLResponse where error == nil {
                if let ct = httpResponse.allHeaderFields["Content-Type"] as? String {
                    completion(type: ct)
                }
            }
        }
        task.resume()
    }
}

getContentType("http://example.com/pic.jpg") { (type) in
    print(type)  // prints "image/jpeg"
}