QNFile.m 3.17 KB
//
//  QNFile.m
//  QiniuSDK
//
//  Created by bailong on 15/7/25.
//  Copyright (c) 2015年 Qiniu. All rights reserved.
//

#import "QNFile.h"
#import "QNResponseInfo.h"

@interface QNFile ()

@property (nonatomic, readonly) NSString *filepath;

@property (nonatomic) NSData *data;

@property (readonly) int64_t fileSize;

@property (readonly) int64_t fileModifyTime;

@property (nonatomic) NSFileHandle *file;

@property (nonatomic) NSLock *lock;

@end

@implementation QNFile

- (instancetype)init:(NSString *)path
               error:(NSError *__autoreleasing *)error {
    if (self = [super init]) {
        _filepath = path;
        NSError *error2 = nil;
        NSDictionary *fileAttr = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error2];
        if (error2 != nil) {
            if (error != nil) {
                *error = error2;
            }
            return self;
        }
        _fileSize = [fileAttr fileSize];
        NSDate *modifyTime = fileAttr[NSFileModificationDate];
        int64_t t = 0;
        if (modifyTime != nil) {
            t = [modifyTime timeIntervalSince1970];
        }
        _fileModifyTime = t;
        NSFileHandle *f = nil;
        NSData *d = nil;
        //[NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error] 不能用在大于 200M的文件上,改用filehandle
        // 参见 https://issues.apache.org/jira/browse/CB-5790
        if (_fileSize > 16 * 1024 * 1024) {
            f = [NSFileHandle fileHandleForReadingAtPath:path];
            if (f == nil) {
                if (error != nil) {
                    *error = [[NSError alloc] initWithDomain:path code:kQNFileError userInfo:nil];
                }
                return self;
            }
        } else {
            d = [NSData dataWithContentsOfFile:path options:NSDataReadingMappedIfSafe error:&error2];
            if (error2 != nil) {
                if (error != nil) {
                    *error = error2;
                }
                return self;
            }
        }
        _file = f;
        _data = d;
        _lock = [[NSLock alloc] init];
    }

    return self;
}

- (NSData *)read:(long)offset
            size:(long)size
           error:(NSError **)error {
    
    NSData *data = nil;
    @try {
        [_lock lock];
        if (_data != nil) {
            data = [_data subdataWithRange:NSMakeRange(offset, (unsigned int)size)];
        } else {
            [_file seekToFileOffset:offset];
            data = [_file readDataOfLength:size];
        }
    } @catch (NSException *exception) {
        *error = [NSError errorWithDomain:NSCocoaErrorDomain code:kQNFileError userInfo:@{NSLocalizedDescriptionKey : exception.reason}];
        NSLog(@"read file failed reason: %@ \n%@", exception.reason, exception.callStackSymbols);
    } @finally {
        [_lock unlock];
    }
    return data;
}

- (NSData *)readAllWithError:(NSError **)error {
    return [self read:0 size:(long)_fileSize error:error];
}

- (void)close {
    if (_file != nil) {
        [_file closeFile];
    }
}

- (NSString *)path {
    return _filepath;
}

- (int64_t)modifyTime {
    return _fileModifyTime;
}

- (int64_t)size {
    return _fileSize;
}

@end