写一个文件管理器并不难,难在如何提升性能?
文件管理器无非就是增删改查
本文的重点在于删和查的新能提升。
普通的目录,我们删除或者查询一个或者多个很快,但如果这个目录文件量达到
举个例子:
1.文件如何归类存储
2.查询目录下超过三个月未访问的文件大小
3.删除超过三个月未访问的文件
针对归类存储,我们的策略很简单,就是语音、图片放到不同的目录下:
/**
* @brief 视频文件目录的路径
*
* @return 视频目录路径
*/
+ (NSString *)videoPath;
/**
* @brief 图片文件目录的路径
*
* @return 图片目录路径
*/
+ (NSString *)imagePath;
/**
* @brief 音频目录路径
*
* @return 音频目录路径
*/
+ (NSString *)audioPath;
针对第二个问题:查询目录下超过三个月未访问的文件大小
我们提供了如下接口:
/**
* @brief 删除图片文件缓存
*
* @return 删除成功失败
*/
+ (BOOL)deleteCachedImages;
+ (BOOL)deleteCachedImagesFor3MonthAgo;
/**
* @brief 删除音频文件缓存
*
* @return 删除成功失败
*/
+ (BOOL)deleteCachedAudioes;
+ (BOOL)deleteCachedAudioesFor3MonthAgo;
/**
* @brief 删除视频文件缓存
*
* @return 删除成功失败
*/
+ (BOOL)deleteCachedVideoes;
+ (BOOL)deleteCachedVideoesFor3MonthAgo;
/**
* @brief 删除3个月前的图片、语音、视频数据
*/
+ (BOOL)deleteCachedForderFor3MonthAgo;
/**
* @brief 删除图片、语音、视频数据
*/
+ (BOOL)deleteCachedForder;
关于第三个问题:删除超过三个月未访问的文件
我们提供接口如下:
/**
* @brief 图片文件夹大小,单位字节B(Byte)
*/
+ (int64_t)imageFolderSize;
/**
* @brief 音频文件夹大小,单位字节B(Byte)
*/
+ (int64_t)audioFolderSize;
/**
* @brief 视频文件夹大小,单位字节B(Byte)
*/
+ (int64_t)videoFolderSize;
/**
* 三个月前的图片、语音、视频文件大小,单位字节B(Byte)
*/
+ (int64_t)folderSizeFor3MonthAgo;
/**
* 图片、语音、视频的文件大小,单位字节B(Byte)
*/
+ (int64_t)cachedFolderSize;
/**
* @brief 系统总容量,单位:B
*/
+ (float)totalDiskSpace;
/**
* @brief 系统剩余可用空间大小,单位:B
*/
+ (float)freeDiskSpace;
/**
* @brief 当前应用使用的硬盘空间,耗时操作,单位:B
*/
+ (float)appUsedSpace;
分割线
下面说说性能,一般情况下为了方便:我们都是用OC代码写,但OC是基于C的封装,我们看看OC如何访问文件的。
// 方法1:使用NSFileManager来实现获取文件大小
+ (long long)fileSizeAtPath1:(NSString *)filePath{
NSFileManager* manager = [NSFileManager defaultManager];
if ([manager fileExistsAtPath:filePath]){
return [[manager attributesOfItemAtPath:filePath error:nil] fileSize];
}
return 0;
}
// 方法1:使用unix c函数来实现获取文件大小
+ (long long)fileSizeAtPath2:(NSString *)filePath{
struct stat st;
if(lstat([filePath cStringUsingEncoding:NSUTF8StringEncoding], &st) == 0){
return st.st_size;
}
return 0;
}
#pragma mark 获取目录大小
//实测 :1000个文件 读取 平均时间为:0.079914
// 方法1:循环调用fileSizeAtPath1
+ (long long)folderSizeAtPath1:(NSString *)folderPath{
CFAbsoluteTime startTime =CFAbsoluteTimeGetCurrent();
NSFileManager* manager = [NSFileManager defaultManager];
if (![manager fileExistsAtPath:folderPath]) return 0;
NSEnumerator *childFilesEnumerator = [[manager subpathsAtPath:folderPath] objectEnumerator];
NSString* fileName;
long long folderSize = 0;
while ((fileName = [childFilesEnumerator nextObject]) != nil){
NSString* fileAbsolutePath = [folderPath stringByAppendingPathComponent:fileName];
folderSize += [self fileSizeAtPath1:fileAbsolutePath];
}
CFAbsoluteTime endTime =CFAbsoluteTimeGetCurrent();
NSLog(@"ttttttttttt:%s,%f,%lld",__func__,endTime-startTime,folderSize);
return folderSize;
}
// 1000个文件平均时间为:0.02653
// 方法2:循环调用fileSizeAtPath2
+ (long long)folderSizeAtPath2:(NSString *)folderPath{
TICK
NSFileManager* manager = [NSFileManager defaultManager];
if (![manager fileExistsAtPath:folderPath]) return 0;
NSEnumerator *childFilesEnumerator = [[manager subpathsAtPath:folderPath] objectEnumerator];
NSString* fileName;
long long folderSize = 0;
while ((fileName = [childFilesEnumerator nextObject]) != nil){
NSString* fileAbsolutePath = [folderPath stringByAppendingPathComponent:fileName];
folderSize += [self fileSizeAtPath2:fileAbsolutePath];
}
TOCK
return folderSize;
}
再来看看如何用c写,其性能如何
// 方法3:完全使用unix c函数 性能最好
//实测 1000个文件速度 0.005896
+ (int64_t)folderSizeAtPath:(const char *)folderPath maxTime:(NSNumber *)maxTime{
CFAbsoluteTime startTime =CFAbsoluteTimeGetCurrent();
int64_t folderSize = 0;
DIR* dir = opendir(folderPath);
if (dir == NULL) return 0;
struct dirent* child;
while ((child = readdir(dir))!=NULL) {
if (child->d_type == DT_DIR && (
(child->d_name[0] == '.' && child->d_name[1] == 0) || // 忽略目录 .
(child->d_name[0] == '.' && child->d_name[1] == '.' && child->d_name[2] == 0) // 忽略目录 ..
)) continue;
int64_t folderPathLength = strlen(folderPath);
// 子文件的路径地址
char childPath[1024];
stpcpy(childPath, folderPath);
if (folderPath[folderPathLength-1] != '/') {
childPath[folderPathLength] = '/';
folderPathLength++;
}
stpcpy(childPath+folderPathLength, child->d_name);
childPath[folderPathLength + child->d_namlen] = 0;
if (child->d_type == DT_DIR) { // directory
// 递归调用子目录
folderSize += [[self class] folderSizeAtPath:childPath maxTime:maxTime];
// 把目录本身所占的空间也加上
struct stat st;
if(lstat(childPath, &st) == 0) folderSize += st.st_size;
}else if (child->d_type == DT_REG || child->d_type == DT_LNK) { // file or link
//特殊处理几个月前的内容
if (maxTime) {
BOOL shouldCountSize = YES;
char *p = child->d_name;
NSString *fileName = [NSString stringWithCString:p encoding:NSUTF8StringEncoding];
NSComparisonResult result = [fileName compare:[maxTime stringValue]];
if (result == NSOrderedDescending)//小于三个月不统计
{
shouldCountSize = NO;
}
if (!shouldCountSize) {
continue;
}
}
struct stat st;
if(lstat(childPath, &st) == 0) folderSize += st.st_size;
}
}
CFAbsoluteTime endTime = CFAbsoluteTimeGetCurrent();
NSLog(@"ttttttttttt:%s,%f,%lld",__func__,endTime-startTime,folderSize);
return folderSize;
}
我们对比一下:
同时访问同样的目录下的1000个文件的用时
方法 | 库 | 用时 |
---|---|---|
方法1 | 使用NSFileManager | 0.079914 |
方法2 | 使用c函数 | 0.02653 |
方法3 | 使用C底层函数 | 0.005896 |
我们看对比:
使用方法3的速度是方法1的 15倍,等于提升了一个数量级。
最后我们还提供了:大小转换为合适的输出
/**
* @brief 获取指定存储小数位数的浮点数,单位G,M,K
* 显示规则如下
* 1.大于1G,转换为G单位的字符串+小数位数
* 2.大于1M,则转化成M单位的字符串+ 小数位数
* 3.不到1M,但是超过了1KB,则转化成KB单位+ 小数位数
* 4.剩下的都是小于1K的,则转化成B单位
*
* @param size 原值大小
* @param decimalplaces 指定小数位数大小
*
* @return 获取指定存储小数位数的浮点数,单位G,M,K
*/
+ (NSString *)getStringWithStorage:(float)size decimalplaces:(NSInteger)decimalplaces;
源码见github地址:WCFileController
本文解释权归:子文
如需转载请注明出处,谢谢
网友评论