QNPHAssetResource.m 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. //
  2. // QNPHAssetResource.m
  3. // QiniuSDK
  4. //
  5. // Created by 何舒 on 16/2/14.
  6. // Copyright © 2016年 Qiniu. All rights reserved.
  7. //
  8. #import "QNPHAssetResource.h"
  9. #import <Photos/Photos.h>
  10. #import "QNResponseInfo.h"
  11. #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000)
  12. enum {
  13. kAMASSETMETADATA_PENDINGREADS = 1,
  14. kAMASSETMETADATA_ALLFINISHED = 0
  15. };
  16. @interface QNPHAssetResource () {
  17. BOOL _hasGotInfo;
  18. }
  19. @property (nonatomic) PHAsset *phAsset;
  20. @property (nonatomic) PHAssetResource *phAssetResource;
  21. @property (nonatomic) int64_t fileSize;
  22. @property (nonatomic) int64_t fileModifyTime;
  23. @property (nonatomic, strong) NSData *assetData;
  24. @property (nonatomic, strong) NSURL *assetURL;
  25. @property (nonatomic, strong) NSLock *lock;
  26. @end
  27. @implementation QNPHAssetResource
  28. - (instancetype)init:(PHAssetResource *)phAssetResource
  29. error:(NSError *__autoreleasing *)error {
  30. if (self = [super init]) {
  31. PHAsset *phasset = [PHAsset fetchAssetsWithBurstIdentifier:self.phAssetResource.assetLocalIdentifier options:nil][0];
  32. NSDate *createTime = phasset.creationDate;
  33. int64_t t = 0;
  34. if (createTime != nil) {
  35. t = [createTime timeIntervalSince1970];
  36. }
  37. _fileModifyTime = t;
  38. _phAssetResource = phAssetResource;
  39. _lock = [[NSLock alloc] init];
  40. [self getInfo];
  41. }
  42. return self;
  43. }
  44. - (NSData *)read:(long long)offset
  45. size:(long)size
  46. error:(NSError **)error {
  47. NSData *data = nil;
  48. @try {
  49. [_lock lock];
  50. if (!self.assetData) {
  51. self.assetData = [self fetchDataFromAsset:self.phAssetResource error:error];
  52. }
  53. if (_assetData != nil && offset < _assetData.length) {
  54. NSUInteger realSize = MIN((NSUInteger)size, _assetData.length - (NSUInteger)offset);
  55. data = [_assetData subdataWithRange:NSMakeRange((NSUInteger)offset, realSize)];
  56. } else {
  57. data = [NSData data];
  58. }
  59. } @catch (NSException *exception) {
  60. *error = [NSError errorWithDomain:NSCocoaErrorDomain code:kQNFileError userInfo:@{NSLocalizedDescriptionKey : exception.reason}];
  61. NSLog(@"read file failed reason: %@ \n%@", exception.reason, exception.callStackSymbols);
  62. } @finally {
  63. [_lock unlock];
  64. }
  65. return data;
  66. }
  67. - (NSData *)readAllWithError:(NSError **)error {
  68. return [self read:0 size:(long)_fileSize error:error];
  69. }
  70. - (void)close {
  71. }
  72. - (NSString *)path {
  73. return self.assetURL.path;
  74. }
  75. - (int64_t)modifyTime {
  76. return _fileModifyTime;
  77. }
  78. - (int64_t)size {
  79. return _fileSize;
  80. }
  81. - (NSString *)fileType {
  82. return @"PHAssetResource";
  83. }
  84. - (void)getInfo {
  85. if (!_hasGotInfo) {
  86. _hasGotInfo = YES;
  87. NSConditionLock *assetReadLock = [[NSConditionLock alloc] initWithCondition:kAMASSETMETADATA_PENDINGREADS];
  88. NSString *fileName = [NSString stringWithFormat:@"tempAsset-%f-%d.mov", [[NSDate date] timeIntervalSince1970], arc4random()%100000];
  89. NSString *pathToWrite = [NSTemporaryDirectory() stringByAppendingString:fileName];
  90. NSURL *localpath = [NSURL fileURLWithPath:pathToWrite];
  91. PHAssetResourceRequestOptions *options = [PHAssetResourceRequestOptions new];
  92. options.networkAccessAllowed = YES;
  93. [[PHAssetResourceManager defaultManager] writeDataForAssetResource:self.phAssetResource toFile:localpath options:options completionHandler:^(NSError *_Nullable error) {
  94. if (error == nil) {
  95. AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:localpath options:nil];
  96. NSNumber *fileSize = nil;
  97. [urlAsset.URL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:nil];
  98. self.fileSize = [fileSize unsignedLongLongValue];
  99. self.assetURL = urlAsset.URL;
  100. self.assetData = [NSData dataWithData:[NSData dataWithContentsOfURL:urlAsset.URL]];
  101. } else {
  102. NSLog(@"%@", error);
  103. }
  104. BOOL blHave = [[NSFileManager defaultManager] fileExistsAtPath:pathToWrite];
  105. if (!blHave) {
  106. return;
  107. } else {
  108. [[NSFileManager defaultManager] removeItemAtPath:pathToWrite error:nil];
  109. }
  110. [assetReadLock lock];
  111. [assetReadLock unlockWithCondition:kAMASSETMETADATA_ALLFINISHED];
  112. }];
  113. [assetReadLock lockWhenCondition:kAMASSETMETADATA_ALLFINISHED];
  114. [assetReadLock unlock];
  115. assetReadLock = nil;
  116. }
  117. }
  118. - (NSData *)fetchDataFromAsset:(PHAssetResource *)videoResource error:(NSError **)err {
  119. __block NSData *tmpData = [NSData data];
  120. __block NSError *innerError = *err;
  121. NSConditionLock *assetReadLock = [[NSConditionLock alloc] initWithCondition:kAMASSETMETADATA_PENDINGREADS];
  122. NSString *fileName = [NSString stringWithFormat:@"tempAsset-%f-%d.mov", [[NSDate date] timeIntervalSince1970], arc4random()%100000];
  123. NSString *pathToWrite = [NSTemporaryDirectory() stringByAppendingString:fileName];
  124. NSURL *localpath = [NSURL fileURLWithPath:pathToWrite];
  125. PHAssetResourceRequestOptions *options = [PHAssetResourceRequestOptions new];
  126. options.networkAccessAllowed = YES;
  127. [[PHAssetResourceManager defaultManager] writeDataForAssetResource:videoResource toFile:localpath options:options completionHandler:^(NSError *_Nullable error) {
  128. if (error == nil) {
  129. AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:localpath options:nil];
  130. NSData *videoData = [NSData dataWithContentsOfURL:urlAsset.URL];
  131. tmpData = [NSData dataWithData:videoData];
  132. } else {
  133. innerError = error;
  134. }
  135. BOOL blHave = [[NSFileManager defaultManager] fileExistsAtPath:pathToWrite];
  136. if (!blHave) {
  137. return;
  138. } else {
  139. [[NSFileManager defaultManager] removeItemAtPath:pathToWrite error:nil];
  140. }
  141. [assetReadLock lock];
  142. [assetReadLock unlockWithCondition:kAMASSETMETADATA_ALLFINISHED];
  143. }];
  144. [assetReadLock lockWhenCondition:kAMASSETMETADATA_ALLFINISHED];
  145. [assetReadLock unlock];
  146. assetReadLock = nil;
  147. return tmpData;
  148. }
  149. @end
  150. #endif