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
165 views
in Technique[技术] by (71.8m points)

ios - Remote image size without downloading

Using Obj-c / Cocoa-touch, I'm trying to get the image size of a list of images but I don't want to download them. Is there some easy way to do it? I found some solutions in other languages like this SO question but I'm looking for a easier way.

Thanks

See Question&Answers more detail:os

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

1 Reply

0 votes
by (71.8m points)

Here is a UIImage category I use for this. It is based on fastimage. One major warning is the NSURLConnectionDelegate is set to the NSURL itself and this may cause clashes in certain situations. This isn't complete yet (file URL's are ignored for example...) but you can see where it is going.

Header:

#import <UIKit/UIKit.h>

typedef void (^UIImageSizeRequestCompleted) (NSURL* imgURL, CGSize size);

@interface UIImage (RemoteSize)

+ (void) requestSizeFor: (NSURL*) imgURL completion: (UIImageSizeRequestCompleted) completion;

@end

Source:

#import "UIImage+RemoteSize.h"

#import <objc/runtime.h>

static char *kSizeRequestDataKey = "NSURL.sizeRequestData";
static char *kSizeRequestTypeKey = "NSURL.sizeRequestType";
static char *kSizeRequestCompletionKey = "NSURL.sizeRequestCompletion";

typedef uint32_t dword;

@interface NSURL (RemoteSize)
@property (nonatomic, strong) NSMutableData* sizeRequestData;
@property (nonatomic, strong) NSString* sizeRequestType;
@property (nonatomic, copy) UIImageSizeRequestCompleted sizeRequestCompletion;
@end

@implementation NSURL (RemoteSize)

- (void) setSizeRequestCompletion: (UIImageSizeRequestCompleted) block {
    objc_setAssociatedObject(self, &kSizeRequestCompletionKey, block, OBJC_ASSOCIATION_COPY);
}

- (UIImageSizeRequestCompleted) sizeRequestCompletion {
    return objc_getAssociatedObject(self, &kSizeRequestCompletionKey);
}

- (void) setSizeRequestData:(NSMutableData *)sizeRequestData {
    objc_setAssociatedObject(self, &kSizeRequestDataKey, sizeRequestData, OBJC_ASSOCIATION_RETAIN);
}

- (NSMutableData*) sizeRequestData {
    return objc_getAssociatedObject(self, &kSizeRequestDataKey);
}

- (void) setSizeRequestType:(NSString *)sizeRequestType {
    objc_setAssociatedObject(self, &kSizeRequestTypeKey, sizeRequestType, OBJC_ASSOCIATION_RETAIN);
}

- (NSString*) sizeRequestType {
    return objc_getAssociatedObject(self, &kSizeRequestTypeKey);
}

#pragma mark - NSURLConnectionDelegate
- (void) connection: (NSURLConnection*) connection didReceiveResponse:(NSURLResponse *)response {
    [self.sizeRequestData setLength: 0];    //Redirected => reset data
}

- (void) connection: (NSURLConnection*) connection didReceiveData:(NSData *)data {
    NSMutableData* receivedData = self.sizeRequestData;

    if( !receivedData ) {
        receivedData = [NSMutableData data];
        self.sizeRequestData = receivedData;
    }

    [receivedData appendData: data];

    //Parse metadata
    const unsigned char* cString = [receivedData bytes];
    const NSInteger length = [receivedData length];

    const char pngSignature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
    const char bmpSignature[2] = {66, 77};
    const char gifSignature[2] = {71, 73}; 
    const char jpgSignature[2] = {255, 216};

    if(!self.sizeRequestType ) {
        if( memcmp(pngSignature, cString, 8) == 0 ) {
            self.sizeRequestType = @"PNG";
        }
        else if( memcmp(bmpSignature, cString, 2) == 0 ) {
            self.sizeRequestType = @"BMP";
        }
        else if( memcmp(jpgSignature, cString, 2) == 0 ) {
            self.sizeRequestType = @"JPG";
        }
        else if( memcmp(gifSignature, cString, 2) == 0 ) {
            self.sizeRequestType = @"GIF";
        }
    }

    if( [self.sizeRequestType isEqualToString: @"PNG"] ) {
        char type[5];
        int offset = 8;

        dword chunkSize = 0;
        int chunkSizeSize = sizeof(chunkSize);

        if( offset+chunkSizeSize > length ) 
            return;

        memcpy(&chunkSize, cString+offset, chunkSizeSize);
        chunkSize = OSSwapInt32(chunkSize);
        offset += chunkSizeSize;

        if( offset + chunkSize > length ) 
            return;

        memcpy(&type, cString+offset, 4); type[4]='';
        offset += 4;

        if( strcmp(type, "IHDR") == 0 ) {   //Should always be first
            dword width = 0, height = 0;
            memcpy(&width, cString+offset, 4);
            offset += 4;
            width = OSSwapInt32(width);

            memcpy(&height, cString+offset, 4);
            offset += 4;
            height = OSSwapInt32(height);

            if( self.sizeRequestCompletion ) {
                self.sizeRequestCompletion(self, CGSizeMake(width, height));
            }

            self.sizeRequestCompletion = nil;

            [connection cancel];
        }
    }
    else if( [self.sizeRequestType isEqualToString: @"BMP"] ) {
        int offset = 18;
        dword width = 0, height = 0;
        memcpy(&width, cString+offset, 4);
        offset += 4;

        memcpy(&height, cString+offset, 4);
        offset += 4;

        if( self.sizeRequestCompletion ) {
            self.sizeRequestCompletion(self, CGSizeMake(width, height));
        }

        self.sizeRequestCompletion = nil;

        [connection cancel];
    }
    else if( [self.sizeRequestType isEqualToString: @"JPG"] ) {
        int offset = 4;
        dword block_length = cString[offset]*256 + cString[offset+1];

        while (offset<length) {
            offset += block_length;

            if( offset >= length )
                break;
            if( cString[offset] != 0xFF )
                break;
            if( cString[offset+1] == 0xC0 ||
                cString[offset+1] == 0xC1 ||
                cString[offset+1] == 0xC2 ||
                cString[offset+1] == 0xC3 ||
                cString[offset+1] == 0xC5 ||
                cString[offset+1] == 0xC6 ||
                cString[offset+1] == 0xC7 ||
                cString[offset+1] == 0xC9 ||
                cString[offset+1] == 0xCA ||
                cString[offset+1] == 0xCB ||
                cString[offset+1] == 0xCD ||
                cString[offset+1] == 0xCE ||
                cString[offset+1] == 0xCF ) {

                dword width = 0, height = 0;

                height = cString[offset+5]*256 + cString[offset+6];
                width = cString[offset+7]*256 + cString[offset+8];

                if( self.sizeRequestCompletion ) {
                    self.sizeRequestCompletion(self, CGSizeMake(width, height));
                }

                self.sizeRequestCompletion = nil;

                [connection cancel];

            }
            else {
                offset += 2;
                block_length = cString[offset]*256 + cString[offset+1];
            }

        }
    }
    else if( [self.sizeRequestType isEqualToString: @"GIF"] ) {
        int offset = 6;
        dword width = 0, height = 0;
        memcpy(&width, cString+offset, 2);
        offset += 2;

        memcpy(&height, cString+offset, 2);
        offset += 2;

        if( self.sizeRequestCompletion ) {
            self.sizeRequestCompletion(self, CGSizeMake(width, height));
        }

        self.sizeRequestCompletion = nil;

        [connection cancel];
    }
}

- (void) connection: (NSURLConnection*) connection didFailWithError:(NSError *)error {
    if( self.sizeRequestCompletion ) 
        self.sizeRequestCompletion(self, CGSizeZero);
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
    return cachedResponse;
}

- (void) connectionDidFinishLoading: (NSURLConnection *)connection {
    // Basically, we failed to obtain the image size using metadata and the
    // entire image was downloaded...

    if(!self.sizeRequestData.length) {
        self.sizeRequestData = nil;
    }
    else {
        //Try parse to UIImage
        UIImage* image = [UIImage imageWithData: self.sizeRequestData];

        if( self.sizeRequestCompletion && image) {
            self.sizeRequestCompletion(self, [image size]);
            return;
        }
    }

    self.sizeRequestCompletion(self, CGSizeZero);
}

@end

@implementation UIImage (RemoteSize)

+ (void) requestSizeFor: (NSURL*) imgURL completion: (UIImageSizeRequestCompleted) completion {

    if( [imgURL isFileURL] ) {
        //Load from file stream
    }
    else {
        imgURL.sizeRequestCompletion = completion;

        NSURLRequest* request = [NSURLRequest requestWithURL: imgURL];
        NSURLConnection* conn = [NSURLConnection connectionWithRequest: request delegate: imgURL];
        [conn scheduleInRunLoop: [NSRunLoop mainRunLoop] forMode: NSDefaultRunLoopMode];
        [conn start];
    }
}

@end

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

...