2014-05-12 115 views
3

我正在使用Dropbox Core API,并且在我正在寻找获取图像文件尺寸的方法时卡住了。我在设备上检索缩略图,但我需要知道图像的宽度和高度以处理它们的修改。通过文件URL远程获取图像文件尺寸

我绝对不想在手机上下载整个文件来检查其尺寸。是否有任何想法获得它们的技巧?我在元数据中唯一的东西是文件大小,在我的情况下这是无用的。

非常感谢。

+0

棘手的问题,但我想除了下载图像和计算自己的大小,没有其他方式。 – satheeshwaran

+0

@satheeshwaran如果您对解决方案感兴趣,请检查答案,避免下载整个文件。可能会有用。不管怎么说,还是要谢谢你。 – BoilingLime

回答

3

我想出了我的答案。我使用UIImage类别,而不是通过URL下载文件的一部分。一旦它获得足够的数据来定义大小,就会停止下载。

我做了一些测试,它下载大约30 kB来获取图片的尺寸,无论文件大小为300 kB还是10 MB大,这真的很快。

它可以用于任何图像文件,而不仅仅是Dropbox API。

这里是该类别的标题:

#import <UIKit/UIKit.h> 

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

@interface UIImage (RemoteSize) 

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

@end 

这里是源文件:

#import "UIImage+RemoteSize.h" 
#import <objc/runtime.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]='\0'; 
    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 

非常感谢这个帖子里面我有很大帮助: Remote image size without downloading

我希望它也能帮助你。

+0

因此,您检索图像URL并将其传递到此类别,然后该类别将获取图像大小。很酷的人,很好的执行。你可以在GitHub上发布它。 – satheeshwaran