Merge pull request #2898 from dreampiggy/feature_extended_metadata_disk_cache
Allows advanced user to read/write extended metadata associated with image data from disk cache
This commit is contained in:
commit
6d7a68e07e
|
@ -89,6 +89,12 @@
|
|||
325C46272233A0A8004CAE11 /* NSBezierPath+RoundedCorners.h in Headers */ = {isa = PBXBuildFile; fileRef = 325C46242233A0A8004CAE11 /* NSBezierPath+RoundedCorners.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
325C46282233A0A8004CAE11 /* NSBezierPath+RoundedCorners.m in Sources */ = {isa = PBXBuildFile; fileRef = 325C46252233A0A8004CAE11 /* NSBezierPath+RoundedCorners.m */; };
|
||||
325C46292233A0A8004CAE11 /* NSBezierPath+RoundedCorners.m in Sources */ = {isa = PBXBuildFile; fileRef = 325C46252233A0A8004CAE11 /* NSBezierPath+RoundedCorners.m */; };
|
||||
325F7CC623893B2E00AEDFCC /* SDFileAttributeHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = 325F7CC423893B2E00AEDFCC /* SDFileAttributeHelper.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||
325F7CC723893B2E00AEDFCC /* SDFileAttributeHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 325F7CC523893B2E00AEDFCC /* SDFileAttributeHelper.m */; };
|
||||
325F7CCA238942AB00AEDFCC /* UIImage+ExtendedCacheData.h in Headers */ = {isa = PBXBuildFile; fileRef = 325F7CC8238942AB00AEDFCC /* UIImage+ExtendedCacheData.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
325F7CCB238942AB00AEDFCC /* UIImage+ExtendedCacheData.m in Sources */ = {isa = PBXBuildFile; fileRef = 325F7CC9238942AB00AEDFCC /* UIImage+ExtendedCacheData.m */; };
|
||||
325F7CCC2389463D00AEDFCC /* UIImage+ExtendedCacheData.m in Sources */ = {isa = PBXBuildFile; fileRef = 325F7CC9238942AB00AEDFCC /* UIImage+ExtendedCacheData.m */; };
|
||||
325F7CCD2389467800AEDFCC /* UIImage+ExtendedCacheData.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 325F7CC8238942AB00AEDFCC /* UIImage+ExtendedCacheData.h */; };
|
||||
326E2F2E236F0B23006F847F /* SDAnimatedImagePlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 326E2F2C236F0B23006F847F /* SDAnimatedImagePlayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
326E2F2F236F0B23006F847F /* SDAnimatedImagePlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 326E2F2D236F0B23006F847F /* SDAnimatedImagePlayer.m */; };
|
||||
326E2F30236F0B23006F847F /* SDAnimatedImagePlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 326E2F2D236F0B23006F847F /* SDAnimatedImagePlayer.m */; };
|
||||
|
@ -298,6 +304,7 @@
|
|||
dstPath = include/SDWebImage;
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
325F7CCD2389467800AEDFCC /* UIImage+ExtendedCacheData.h in Copy Headers */,
|
||||
326E2F36236F1E30006F847F /* SDAnimatedImagePlayer.h in Copy Headers */,
|
||||
3250C9F12355E3DF0093A896 /* SDWebImageDownloaderDecryptor.h in Copy Headers */,
|
||||
325427662355783C0042BAA4 /* SDWebImageDownloaderResponseModifier.h in Copy Headers */,
|
||||
|
@ -407,6 +414,10 @@
|
|||
325C461F2233A02E004CAE11 /* UIColor+HexString.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIColor+HexString.m"; sourceTree = "<group>"; };
|
||||
325C46242233A0A8004CAE11 /* NSBezierPath+RoundedCorners.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSBezierPath+RoundedCorners.h"; sourceTree = "<group>"; };
|
||||
325C46252233A0A8004CAE11 /* NSBezierPath+RoundedCorners.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSBezierPath+RoundedCorners.m"; sourceTree = "<group>"; };
|
||||
325F7CC423893B2E00AEDFCC /* SDFileAttributeHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDFileAttributeHelper.h; sourceTree = "<group>"; };
|
||||
325F7CC523893B2E00AEDFCC /* SDFileAttributeHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDFileAttributeHelper.m; sourceTree = "<group>"; };
|
||||
325F7CC8238942AB00AEDFCC /* UIImage+ExtendedCacheData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "UIImage+ExtendedCacheData.h"; path = "Core/UIImage+ExtendedCacheData.h"; sourceTree = "<group>"; };
|
||||
325F7CC9238942AB00AEDFCC /* UIImage+ExtendedCacheData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "UIImage+ExtendedCacheData.m"; path = "Core/UIImage+ExtendedCacheData.m"; sourceTree = "<group>"; };
|
||||
326E2F2C236F0B23006F847F /* SDAnimatedImagePlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDAnimatedImagePlayer.h; path = Core/SDAnimatedImagePlayer.h; sourceTree = "<group>"; };
|
||||
326E2F2D236F0B23006F847F /* SDAnimatedImagePlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDAnimatedImagePlayer.m; path = Core/SDAnimatedImagePlayer.m; sourceTree = "<group>"; };
|
||||
326E2F31236F1D58006F847F /* SDDeviceHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDDeviceHelper.h; sourceTree = "<group>"; };
|
||||
|
@ -635,6 +646,8 @@
|
|||
325C461F2233A02E004CAE11 /* UIColor+HexString.m */,
|
||||
325C46242233A0A8004CAE11 /* NSBezierPath+RoundedCorners.h */,
|
||||
325C46252233A0A8004CAE11 /* NSBezierPath+RoundedCorners.m */,
|
||||
325F7CC423893B2E00AEDFCC /* SDFileAttributeHelper.h */,
|
||||
325F7CC523893B2E00AEDFCC /* SDFileAttributeHelper.m */,
|
||||
329F123F223FAD3400B309FD /* SDInternalMacros.h */,
|
||||
329F123E223FAD3400B309FD /* SDInternalMacros.m */,
|
||||
329F1235223FAA3B00B309FD /* SDmetamacros.h */,
|
||||
|
@ -744,6 +757,8 @@
|
|||
children = (
|
||||
5D5B9140188EE8DD006D06BD /* NSData+ImageContentType.h */,
|
||||
5D5B9141188EE8DD006D06BD /* NSData+ImageContentType.m */,
|
||||
325F7CC8238942AB00AEDFCC /* UIImage+ExtendedCacheData.h */,
|
||||
325F7CC9238942AB00AEDFCC /* UIImage+ExtendedCacheData.m */,
|
||||
A18A6CC5172DC28500419892 /* UIImage+GIF.h */,
|
||||
A18A6CC6172DC28500419892 /* UIImage+GIF.m */,
|
||||
329A18571FFF5DFD008C9A2F /* UIImage+Metadata.h */,
|
||||
|
@ -849,6 +864,7 @@
|
|||
3257EAFA21898AED0097B271 /* SDImageGraphics.h in Headers */,
|
||||
32D3CDD121DDE87300C4DB49 /* UIImage+MemoryCacheCost.h in Headers */,
|
||||
328BB6AC2081FEE500760D6C /* SDWebImageCacheSerializer.h in Headers */,
|
||||
325F7CCA238942AB00AEDFCC /* UIImage+ExtendedCacheData.h in Headers */,
|
||||
325C46272233A0A8004CAE11 /* NSBezierPath+RoundedCorners.h in Headers */,
|
||||
321B378F2083290E00C0EA77 /* SDImageLoadersManager.h in Headers */,
|
||||
329A185B1FFF5DFD008C9A2F /* UIImage+Metadata.h in Headers */,
|
||||
|
@ -906,6 +922,7 @@
|
|||
32C0FDE32013426C001B8F2D /* SDWebImageIndicator.h in Headers */,
|
||||
32F7C0712030114C00873181 /* SDImageTransformer.h in Headers */,
|
||||
32E67311235765B500DB4987 /* SDDisplayLink.h in Headers */,
|
||||
325F7CC623893B2E00AEDFCC /* SDFileAttributeHelper.h in Headers */,
|
||||
4A2CAE2D1AB4BB7500B6BC39 /* UIImage+GIF.h in Headers */,
|
||||
4A2CAE291AB4BB7500B6BC39 /* NSData+ImageContentType.h in Headers */,
|
||||
328BB69E2081FED200760D6C /* SDWebImageCacheKeyFilter.h in Headers */,
|
||||
|
@ -1098,6 +1115,7 @@
|
|||
3257EAFD21898AED0097B271 /* SDImageGraphics.m in Sources */,
|
||||
3290FA0C1FA478AF0047D20C /* SDImageFrame.m in Sources */,
|
||||
325C46232233A02E004CAE11 /* UIColor+HexString.m in Sources */,
|
||||
325F7CCB238942AB00AEDFCC /* UIImage+ExtendedCacheData.m in Sources */,
|
||||
321E60C61F38E91700405457 /* UIImage+ForceDecode.m in Sources */,
|
||||
3244062E2296C5F400A36084 /* SDWebImageOptionsProcessor.m in Sources */,
|
||||
3250C9F02355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */,
|
||||
|
@ -1127,6 +1145,7 @@
|
|||
321E609C1F38E8ED00405457 /* SDImageIOCoder.m in Sources */,
|
||||
4A2CAE261AB4BB7000B6BC39 /* SDWebImagePrefetcher.m in Sources */,
|
||||
328BB6C92082581100760D6C /* SDDiskCache.m in Sources */,
|
||||
325F7CC723893B2E00AEDFCC /* SDFileAttributeHelper.m in Sources */,
|
||||
3248475F201775F600AF9E5A /* SDAnimatedImageView.m in Sources */,
|
||||
32D1222C2080B2EB003685A3 /* SDImageCachesManager.m in Sources */,
|
||||
32B9B53F206ED4230026769D /* SDWebImageDownloaderConfig.m in Sources */,
|
||||
|
@ -1198,6 +1217,7 @@
|
|||
5376130D155AD0D5005750A4 /* SDWebImagePrefetcher.m in Sources */,
|
||||
328BB6C72082581100760D6C /* SDDiskCache.m in Sources */,
|
||||
3248475D201775F600AF9E5A /* SDAnimatedImageView.m in Sources */,
|
||||
325F7CCC2389463D00AEDFCC /* UIImage+ExtendedCacheData.m in Sources */,
|
||||
32D1222A2080B2EB003685A3 /* SDImageCachesManager.m in Sources */,
|
||||
32B9B53D206ED4230026769D /* SDWebImageDownloaderConfig.m in Sources */,
|
||||
43A9186B1D8308FE00B3925F /* SDImageCacheConfig.m in Sources */,
|
||||
|
|
|
@ -54,6 +54,26 @@
|
|||
*/
|
||||
- (void)setData:(nullable NSData *)data forKey:(nonnull NSString *)key;
|
||||
|
||||
/**
|
||||
Returns the extended data associated with a given key.
|
||||
This method may blocks the calling thread until file read finished.
|
||||
|
||||
@param key A string identifying the data. If nil, just return nil.
|
||||
@return The value associated with key, or nil if no value is associated with key.
|
||||
*/
|
||||
- (nullable NSData *)extendedDataForKey:(nonnull NSString *)key;
|
||||
|
||||
/**
|
||||
Set extended data with a given key.
|
||||
|
||||
@discussion You can set any extended data to exist cache key. Without override the exist disk file data.
|
||||
on UNIX, the common way for this is to use the Extended file attributes (xattr)
|
||||
|
||||
@param extendedData The extended data (pass nil to remove).
|
||||
@param key The key with which to associate the value. If nil, this method has no effect.
|
||||
*/
|
||||
- (void)setExtendedData:(nullable NSData *)extendedData forKey:(nonnull NSString *)key;
|
||||
|
||||
/**
|
||||
Removes the value of the specified key in the cache.
|
||||
This method may blocks the calling thread until file delete finished.
|
||||
|
|
|
@ -8,8 +8,11 @@
|
|||
|
||||
#import "SDDiskCache.h"
|
||||
#import "SDImageCacheConfig.h"
|
||||
#import "SDFileAttributeHelper.h"
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
static NSString * const SDDiskCacheExtendedAttributeName = @"com.hackemist.SDDiskCache";
|
||||
|
||||
@interface SDDiskCache ()
|
||||
|
||||
@property (nonatomic, copy) NSString *diskCachePath;
|
||||
|
@ -95,6 +98,31 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (NSData *)extendedDataForKey:(NSString *)key {
|
||||
NSParameterAssert(key);
|
||||
|
||||
// get cache Path for image key
|
||||
NSString *cachePathForKey = [self cachePathForKey:key];
|
||||
|
||||
NSData *extendedData = [SDFileAttributeHelper extendedAttribute:SDDiskCacheExtendedAttributeName atPath:cachePathForKey traverseLink:NO error:nil];
|
||||
|
||||
return extendedData;
|
||||
}
|
||||
|
||||
- (void)setExtendedData:(NSData *)extendedData forKey:(NSString *)key {
|
||||
NSParameterAssert(key);
|
||||
// get cache Path for image key
|
||||
NSString *cachePathForKey = [self cachePathForKey:key];
|
||||
|
||||
if (!extendedData) {
|
||||
// Remove
|
||||
[SDFileAttributeHelper removeExtendedAttribute:SDDiskCacheExtendedAttributeName atPath:cachePathForKey traverseLink:NO error:nil];
|
||||
} else {
|
||||
// Override
|
||||
[SDFileAttributeHelper setExtendedAttribute:SDDiskCacheExtendedAttributeName value:extendedData atPath:cachePathForKey traverseLink:NO overwrite:YES error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeDataForKey:(NSString *)key {
|
||||
NSParameterAssert(key);
|
||||
NSString *filePath = [self cachePathForKey:key];
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#import "SDAnimatedImage.h"
|
||||
#import "UIImage+MemoryCacheCost.h"
|
||||
#import "UIImage+Metadata.h"
|
||||
#import "UIImage+ExtendedCacheData.h"
|
||||
|
||||
@interface SDImageCache ()
|
||||
|
||||
|
@ -197,6 +198,29 @@
|
|||
data = [[SDImageCodersManager sharedManager] encodedDataWithImage:image format:format options:nil];
|
||||
}
|
||||
[self _storeImageDataToDisk:data forKey:key];
|
||||
if (image) {
|
||||
// Check extended data
|
||||
id extendedObject = image.sd_extendedObject;
|
||||
if ([extendedObject conformsToProtocol:@protocol(NSCoding)]) {
|
||||
NSData *extendedData;
|
||||
if (@available(iOS 11, tvOS 11, macOS 10.13, watchOS 4, *)) {
|
||||
NSError *error;
|
||||
extendedData = [NSKeyedArchiver archivedDataWithRootObject:extendedObject requiringSecureCoding:NO error:&error];
|
||||
if (error) {
|
||||
NSLog(@"NSKeyedArchiver archive failed with error: %@", error);
|
||||
}
|
||||
} else {
|
||||
@try {
|
||||
extendedData = [NSKeyedArchiver archivedDataWithRootObject:extendedObject];
|
||||
} @catch (NSException *exception) {
|
||||
NSLog(@"NSKeyedArchiver archive failed with exception: %@", exception);
|
||||
}
|
||||
}
|
||||
if (extendedData) {
|
||||
[self.diskCache setExtendedData:extendedData forKey:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (completionBlock) {
|
||||
|
@ -346,6 +370,29 @@
|
|||
- (nullable UIImage *)diskImageForKey:(nullable NSString *)key data:(nullable NSData *)data options:(SDImageCacheOptions)options context:(SDWebImageContext *)context {
|
||||
if (data) {
|
||||
UIImage *image = SDImageCacheDecodeImageData(data, key, [[self class] imageOptionsFromCacheOptions:options], context);
|
||||
if (image) {
|
||||
// Check extended data
|
||||
NSData *extendedData = [self.diskCache extendedDataForKey:key];
|
||||
if (extendedData) {
|
||||
id extendedObject;
|
||||
if (@available(iOS 11, tvOS 11, macOS 10.13, watchOS 4, *)) {
|
||||
NSError *error;
|
||||
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:extendedData error:&error];
|
||||
unarchiver.requiresSecureCoding = NO;
|
||||
extendedObject = [unarchiver decodeTopLevelObjectForKey:NSKeyedArchiveRootObjectKey error:&error];
|
||||
if (error) {
|
||||
NSLog(@"NSKeyedUnarchiver unarchive failed with error: %@", error);
|
||||
}
|
||||
} else {
|
||||
@try {
|
||||
extendedObject = [NSKeyedUnarchiver unarchiveObjectWithData:extendedData];
|
||||
} @catch (NSException *exception) {
|
||||
NSLog(@"NSKeyedUnarchiver unarchive failed with exception: %@", exception);
|
||||
}
|
||||
}
|
||||
image.sd_extendedObject = extendedObject;
|
||||
}
|
||||
}
|
||||
return image;
|
||||
} else {
|
||||
return nil;
|
||||
|
|
|
@ -17,6 +17,10 @@ typedef NSData * _Nullable(^SDWebImageCacheSerializerBlock)(UIImage * _Nonnull i
|
|||
*/
|
||||
@protocol SDWebImageCacheSerializer <NSObject>
|
||||
|
||||
/// Provide the image data associated to the image and store to disk cache
|
||||
/// @param image The loaded image
|
||||
/// @param data The original loaded image data
|
||||
/// @param imageURL The image URL
|
||||
- (nullable NSData *)cacheDataWithImage:(nonnull UIImage *)image originalData:(nullable NSData *)data imageURL:(nullable NSURL *)imageURL;
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#import "SDWebImageDefine.h"
|
||||
#import "UIImage+Metadata.h"
|
||||
#import "NSImage+Compatibility.h"
|
||||
#import "UIImage+ExtendedCacheData.h"
|
||||
|
||||
#pragma mark - Image scale
|
||||
|
||||
|
@ -112,6 +113,7 @@ inline UIImage * _Nullable SDScaledImageForScaleFactor(CGFloat scale, UIImage *
|
|||
}
|
||||
scaledImage.sd_isIncremental = image.sd_isIncremental;
|
||||
scaledImage.sd_imageFormat = image.sd_imageFormat;
|
||||
scaledImage.sd_extendedObject = image.sd_extendedObject;
|
||||
|
||||
return scaledImage;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#import "SDImageCache.h"
|
||||
#import "SDWebImageDownloader.h"
|
||||
#import "UIImage+Metadata.h"
|
||||
#import "UIImage+ExtendedCacheData.h"
|
||||
#import "SDWebImageError.h"
|
||||
#import "SDInternalMacros.h"
|
||||
|
||||
|
@ -341,6 +342,10 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
} else {
|
||||
cacheData = (imageWasTransformed ? nil : downloadedData);
|
||||
}
|
||||
// keep the original image format and extended data
|
||||
transformedImage.sd_isIncremental = downloadedImage.sd_isIncremental;
|
||||
transformedImage.sd_imageFormat = downloadedImage.sd_imageFormat;
|
||||
transformedImage.sd_extendedObject = downloadedImage.sd_extendedObject;
|
||||
[self.imageCache storeImage:transformedImage imageData:cacheData forKey:cacheKey cacheType:storeCacheType completion:nil];
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* This file is part of the SDWebImage package.
|
||||
* (c) Olivier Poitrey <rs@dailymotion.com>
|
||||
* (c) Fabrice Aneche
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "SDWebImageCompat.h"
|
||||
|
||||
@interface UIImage (ExtendedCacheData)
|
||||
|
||||
/**
|
||||
Read and Write the extended object and bind it to the image. Which can hold some extra metadata like Image's scale factor, URL rich link, date, etc.
|
||||
The extended object should conforms to NSCoding, which we use `NSKeyedArchiver` and `NSKeyedUnarchiver` to archive it to data, and write to disk cache.
|
||||
@note The disk cache preserve both of the data and extended data with the same cache key. For manual query, use the `SDDiskCache` protocol method `extendedDataForKey:` instead.
|
||||
@note You can specify arbitrary object conforms to NSCoding (NSObject protocol here is used to support object using `NS_ROOT_CLASS`, which is not NSObject subclass). If you load image from disk cache, you should check the extended object class to avoid corrupted data.
|
||||
@warning This object don't need to implements NSSecureCoding (but it's recommended), because we allows arbitrary class.
|
||||
*/
|
||||
@property (nonatomic, strong, nullable) id<NSObject, NSCoding> sd_extendedObject;
|
||||
|
||||
@end
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* This file is part of the SDWebImage package.
|
||||
* (c) Olivier Poitrey <rs@dailymotion.com>
|
||||
* (c) Fabrice Aneche
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
#import "UIImage+ExtendedCacheData.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@implementation UIImage (ExtendedCacheData)
|
||||
|
||||
- (id<NSObject, NSCoding>)sd_extendedObject {
|
||||
return objc_getAssociatedObject(self, @selector(sd_extendedObject));
|
||||
}
|
||||
|
||||
- (void)setSd_extendedObject:(id<NSObject, NSCoding>)sd_extendedObject {
|
||||
objc_setAssociatedObject(self, @selector(sd_extendedObject), sd_extendedObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// This file is from https://gist.github.com/zydeco/6292773
|
||||
//
|
||||
// Created by Jesús A. Álvarez on 2008-12-17.
|
||||
// Copyright 2008-2009 namedfork.net. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface SDFileAttributeHelper : NSObject
|
||||
|
||||
+ (NSArray*)extendedAttributeNamesAtPath:(NSString*)path traverseLink:(BOOL)follow error:(NSError**)err;
|
||||
+ (BOOL)hasExtendedAttribute:(NSString*)name atPath:(NSString*)path traverseLink:(BOOL)follow error:(NSError**)err;
|
||||
+ (NSData*)extendedAttribute:(NSString*)name atPath:(NSString*)path traverseLink:(BOOL)follow error:(NSError**)err;
|
||||
+ (BOOL)setExtendedAttribute:(NSString*)name value:(NSData*)value atPath:(NSString*)path traverseLink:(BOOL)follow overwrite:(BOOL)overwrite error:(NSError**)err;
|
||||
+ (BOOL)removeExtendedAttribute:(NSString*)name atPath:(NSString*)path traverseLink:(BOOL)follow error:(NSError**)err;
|
||||
|
||||
@end
|
|
@ -0,0 +1,127 @@
|
|||
//
|
||||
// This file is from https://gist.github.com/zydeco/6292773
|
||||
//
|
||||
// Created by Jesús A. Álvarez on 2008-12-17.
|
||||
// Copyright 2008-2009 namedfork.net. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SDFileAttributeHelper.h"
|
||||
#import <sys/xattr.h>
|
||||
|
||||
@implementation SDFileAttributeHelper
|
||||
|
||||
+ (NSArray*)extendedAttributeNamesAtPath:(NSString*)path traverseLink:(BOOL)follow error:(NSError**)err {
|
||||
int flags = follow? 0 : XATTR_NOFOLLOW;
|
||||
|
||||
// get size of name list
|
||||
ssize_t nameBuffLen = listxattr([path fileSystemRepresentation], NULL, 0, flags);
|
||||
if (nameBuffLen == -1) {
|
||||
if (err) *err = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithUTF8String:strerror(errno)], @"error",
|
||||
@"listxattr", @"function",
|
||||
path, @":path",
|
||||
[NSNumber numberWithBool:follow], @":traverseLink",
|
||||
nil]
|
||||
];
|
||||
return nil;
|
||||
} else if (nameBuffLen == 0) return [NSArray array];
|
||||
|
||||
// get name list
|
||||
NSMutableData *nameBuff = [NSMutableData dataWithLength:nameBuffLen];
|
||||
listxattr([path fileSystemRepresentation], [nameBuff mutableBytes], nameBuffLen, flags);
|
||||
|
||||
// convert to array
|
||||
NSMutableArray * names = [NSMutableArray arrayWithCapacity:5];
|
||||
char *nextName, *endOfNames = [nameBuff mutableBytes] + nameBuffLen;
|
||||
for(nextName = [nameBuff mutableBytes]; nextName < endOfNames; nextName += 1+strlen(nextName))
|
||||
[names addObject:[NSString stringWithUTF8String:nextName]];
|
||||
return [NSArray arrayWithArray:names];
|
||||
}
|
||||
|
||||
+ (BOOL)hasExtendedAttribute:(NSString*)name atPath:(NSString*)path traverseLink:(BOOL)follow error:(NSError**)err {
|
||||
int flags = follow? 0 : XATTR_NOFOLLOW;
|
||||
|
||||
// get size of name list
|
||||
ssize_t nameBuffLen = listxattr([path fileSystemRepresentation], NULL, 0, flags);
|
||||
if (nameBuffLen == -1) {
|
||||
if (err) *err = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithUTF8String:strerror(errno)], @"error",
|
||||
@"listxattr", @"function",
|
||||
path, @":path",
|
||||
[NSNumber numberWithBool:follow], @":traverseLink",
|
||||
nil]
|
||||
];
|
||||
return NO;
|
||||
} else if (nameBuffLen == 0) return NO;
|
||||
|
||||
// get name list
|
||||
NSMutableData *nameBuff = [NSMutableData dataWithLength:nameBuffLen];
|
||||
listxattr([path fileSystemRepresentation], [nameBuff mutableBytes], nameBuffLen, flags);
|
||||
|
||||
// find our name
|
||||
char *nextName, *endOfNames = [nameBuff mutableBytes] + nameBuffLen;
|
||||
for(nextName = [nameBuff mutableBytes]; nextName < endOfNames; nextName += 1+strlen(nextName))
|
||||
if (strcmp(nextName, [name UTF8String]) == 0) return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (NSData*)extendedAttribute:(NSString*)name atPath:(NSString*)path traverseLink:(BOOL)follow error:(NSError**)err {
|
||||
int flags = follow? 0 : XATTR_NOFOLLOW;
|
||||
// get length
|
||||
ssize_t attrLen = getxattr([path fileSystemRepresentation], [name UTF8String], NULL, 0, 0, flags);
|
||||
if (attrLen == -1) {
|
||||
if (err) *err = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithUTF8String:strerror(errno)], @"error",
|
||||
@"getxattr", @"function",
|
||||
name, @":name",
|
||||
path, @":path",
|
||||
[NSNumber numberWithBool:follow], @":traverseLink",
|
||||
nil]
|
||||
];
|
||||
return nil;
|
||||
}
|
||||
|
||||
// get attribute data
|
||||
NSMutableData * attrData = [NSMutableData dataWithLength:attrLen];
|
||||
getxattr([path fileSystemRepresentation], [name UTF8String], [attrData mutableBytes], attrLen, 0, flags);
|
||||
return attrData;
|
||||
}
|
||||
|
||||
+ (BOOL)setExtendedAttribute:(NSString*)name value:(NSData*)value atPath:(NSString*)path traverseLink:(BOOL)follow overwrite:(BOOL)overwrite error:(NSError**)err {
|
||||
int flags = (follow? 0 : XATTR_NOFOLLOW) | (overwrite? 0 : XATTR_CREATE);
|
||||
if (0 == setxattr([path fileSystemRepresentation], [name UTF8String], [value bytes], [value length], 0, flags)) return YES;
|
||||
// error
|
||||
if (err) *err = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithUTF8String:strerror(errno)], @"error",
|
||||
@"setxattr", @"function",
|
||||
name, @":name",
|
||||
[NSNumber numberWithUnsignedInteger:[value length]], @":value.length",
|
||||
path, @":path",
|
||||
[NSNumber numberWithBool:follow], @":traverseLink",
|
||||
[NSNumber numberWithBool:overwrite], @":overwrite",
|
||||
nil]
|
||||
];
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)removeExtendedAttribute:(NSString*)name atPath:(NSString*)path traverseLink:(BOOL)follow error:(NSError**)err {
|
||||
int flags = (follow? 0 : XATTR_NOFOLLOW);
|
||||
if (0 == removexattr([path fileSystemRepresentation], [name UTF8String], flags)) return YES;
|
||||
// error
|
||||
if (err) *err = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:
|
||||
[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSString stringWithUTF8String:strerror(errno)], @"error",
|
||||
@"removexattr", @"function",
|
||||
name, @":name",
|
||||
path, @":path",
|
||||
[NSNumber numberWithBool:follow], @":traverseLink",
|
||||
nil]
|
||||
];
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
|
@ -491,6 +491,28 @@ static NSString *kTestImageKeyPNG = @"TestImageKey.png";
|
|||
}
|
||||
#endif
|
||||
|
||||
- (void)test47DiskCacheExtendedData {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"SDImageCache extended data read/write works"];
|
||||
UIImage *image = [self testPNGImage];
|
||||
NSDictionary *extendedObject = @{@"Test" : @"Object"};
|
||||
image.sd_extendedObject = extendedObject;
|
||||
[SDImageCache.sharedImageCache removeImageFromMemoryForKey:kTestImageKeyPNG];
|
||||
[SDImageCache.sharedImageCache removeImageFromDiskForKey:kTestImageKeyPNG];
|
||||
// Write extended data
|
||||
[SDImageCache.sharedImageCache storeImage:image forKey:kTestImageKeyPNG completion:^{
|
||||
NSData *extendedData = [SDImageCache.sharedImageCache.diskCache extendedDataForKey:kTestImageKeyPNG];
|
||||
expect(extendedData).toNot.beNil();
|
||||
// Read extended data
|
||||
UIImage *newImage = [SDImageCache.sharedImageCache imageFromDiskCacheForKey:kTestImageKeyPNG];
|
||||
id newExtendedObject = newImage.sd_extendedObject;
|
||||
expect(extendedObject).equal(newExtendedObject);
|
||||
// Remove extended data
|
||||
[SDImageCache.sharedImageCache.diskCache setExtendedData:nil forKey:kTestImageKeyPNG];
|
||||
[expectation fulfill];
|
||||
}];
|
||||
[self waitForExpectationsWithCommonTimeout];
|
||||
}
|
||||
|
||||
#pragma mark - SDImageCache & SDImageCachesManager
|
||||
- (void)test50SDImageCacheQueryOp {
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"SDImageCache query op works"];
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#import "SDWeakProxy.h"
|
||||
#import "SDDisplayLink.h"
|
||||
#import "SDInternalMacros.h"
|
||||
#import "SDFileAttributeHelper.h"
|
||||
|
||||
@interface SDUtilsTests : SDTestCase
|
||||
|
||||
|
@ -74,6 +75,38 @@
|
|||
expect(duration).beLessThan(0.02);
|
||||
}
|
||||
|
||||
- (void)testSDFileAttributeHelper {
|
||||
NSData *fileData = [@"File Data" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *extendedData = [@"Extended Data" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *filePath = @"/tmp/file.dat";
|
||||
[NSFileManager.defaultManager removeItemAtPath:filePath error:nil];
|
||||
[fileData writeToFile:filePath atomically:YES];
|
||||
BOOL exist = [NSFileManager.defaultManager fileExistsAtPath:filePath];
|
||||
expect(exist).beTruthy();
|
||||
|
||||
NSArray *names = [SDFileAttributeHelper extendedAttributeNamesAtPath:filePath traverseLink:NO error:nil];
|
||||
expect(names.count).equal(0);
|
||||
|
||||
NSString *attr = @"com.hackemist.test";
|
||||
[SDFileAttributeHelper setExtendedAttribute:attr value:extendedData atPath:filePath traverseLink:NO overwrite:YES error:nil];
|
||||
|
||||
BOOL hasAttr =[SDFileAttributeHelper hasExtendedAttribute:attr atPath:filePath traverseLink:NO error:nil];
|
||||
expect(hasAttr).beTruthy();
|
||||
|
||||
names = [SDFileAttributeHelper extendedAttributeNamesAtPath:filePath traverseLink:NO error:nil];
|
||||
expect(names.count).equal(1);
|
||||
expect(names.firstObject).equal(attr);
|
||||
|
||||
NSData *queriedData = [SDFileAttributeHelper extendedAttribute:attr atPath:filePath traverseLink:NO error:nil];
|
||||
expect(extendedData).equal(queriedData);
|
||||
|
||||
BOOL removed = [SDFileAttributeHelper removeExtendedAttribute:attr atPath:filePath traverseLink:NO error:nil];
|
||||
expect(removed).beTruthy();
|
||||
|
||||
hasAttr = [SDFileAttributeHelper hasExtendedAttribute:attr atPath:filePath traverseLink:NO error:nil];
|
||||
expect(hasAttr).beFalsy();
|
||||
}
|
||||
|
||||
- (void)testSDScaledImageForKey {
|
||||
// Test nil
|
||||
expect(SDScaledImageForKey(nil, nil)).beNil();
|
||||
|
|
|
@ -46,6 +46,7 @@ FOUNDATION_EXPORT const unsigned char WebImageVersionString[];
|
|||
#import <SDWebImage/UIImage+Metadata.h>
|
||||
#import <SDWebImage/UIImage+MultiFormat.h>
|
||||
#import <SDWebImage/UIImage+MemoryCacheCost.h>
|
||||
#import <SDWebImage/UIImage+ExtendedCacheData.h>
|
||||
#import <SDWebImage/SDWebImageOperation.h>
|
||||
#import <SDWebImage/SDWebImageDownloader.h>
|
||||
#import <SDWebImage/SDWebImageTransition.h>
|
||||
|
|
Loading…
Reference in New Issue