iOS开发——数据的保存与导出,以及不定参数的使用和图片的简便批量读取

一、写在前面

有段时间没有更新内容,前阵子回学校参加专业的实训,没有什么好写的。这篇文章主要记录一下学习沙盒、数据存储与导出的过程,与学习的总结,以及能够提供一个完整的demo,用于新知识的学习。
2020年2月12日,周三。更新日志:第二次重写,上次页面崩溃,现在时间晚上7点21分。
愿疫情早日过去,一切平安。
2020年2月12日,第三次崩溃,提前保存了一次。
电脑端查看沙盒内容

二、数据的存储与导出

2.1 沙盒介绍

iOS中的沙盒机制是一种安全体系。为了保证系统安全,iOS每个应用程序在安装时,会创建属于自己的沙盒文件(存储空间)。应用程序只能访问自身的沙盒文件,不能访问其他应用程序的沙盒文件,当应用程序需要向外部请求或接收数据时,都需要经过权限认证,否则,无法获取到数据。所有的非代码文件都要保存在此,例如属性文件plist、文本文件、图像、图标、媒体资源等,其原理是通过重定向技术,把程序生成和修改的文件定向到自身文件夹中。

2.1.1 沙盒机制

  • 每一个应用程序都会拥有一个应用程序沙盒
  • 应用程序沙盒就是一个文件系统目录

2.1.2 iOS中的沙盒机制

  • 沙盒是一种安全体系
  • TA规定了应用程序只能在为该程序创建的文件夹(沙盒)内访问文件,不可以访问其他沙盒内的内容(iOS8已经部分开放访问)
  • 所有的非代码文件都保存在这个地方,比如图片、音乐、属性列表(plist)、sqlite数据库和文本文件等

2.1.3 沙盒机制的特点

  • 每个应用程序的活动范围都限定在自己的沙盒里面
  • 不能随意跨越自己的沙盒去访问别的应用程序沙盒中的内容(iOS已经部分开放访问)
  • 应用程序向外请求或接受数据都需要经过权限认证

2.2 沙盒中的文件夹

  • Documents:保存应用运行时生成的需要持久化的数据,iTunes会自动备份该目录
  • Library:存储程序的默认设置和其他状态信息,iTunes会自动备份该目录。
  • Library/Caches:存放缓存文件,iTunes不会备份此目录,次目录下文件不会在应用退出删除。
  • Library/Preferences:保存应用的所有偏好设置,iOS的Settings(设置)应用会在该目录中查找应用的设置信息,iTunes会自动备份该目录。注意:你不会直接创建偏好设置文件,二十应该使用NSUserDefaults类来获得和设置应用程序的偏好
  • tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也有可能会清楚该目录下的文件,iTunes不会同步该目录。iphone重启时,该目录下的文件会被删除

2.2.1 获取Documents目录

 NSString *path = [[NSString NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

2.2.2 获取tmp目录

NSString *tmpPath  = NSTemporaryDirectory();

2.2.3 获取Library目录

NSString *libPath = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject];

2.2.4 获取Library/Caches目录

NSString *cachePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask, YES) firstObject];

2.2.5 获取Library/Preferences目录

NSString *libPath = [[NSSeachForPathDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject];
NSString *prePath = [libPath stringByAppendingPathComponent:@"Preferences"];

2.2.6 应用程序包所在位置

NSString *path = [NSBundle mainBundle].resourcePth;

2.3 数据存储与读取

iOS中提供4种类型可以直接进行文件存取

NSString、NSArray、NSDictionary、NSData

2.3.1 字符串写入沙盒

// 在Documents下面创建一个文本路径,假设文本名称为bada.txt
NSString *txtPath = [docPath stringByAppendingPathComponent:@"az.txt"];// 此时仅存在路径,文件并没有真实存在
NSString *string = @"Hello World";
[string writeToFile:txtPath atomically:YES encoding:NSUTF8StringEncoding error:nil];// 字符串写入时执行的代码
NSLog(@"%@", txtPath);

2.3.2 从文件中读取字符串的方法

NSString *resultString = [NSString stringWithContentsOfFile:txtPath encoding:NSUTF8StringEncoding error:nil];
NSLog(@"%@", resultString);

2.3.3 数组写入文件

// 创建一个存储数组的文件路径
NSString *filePath = [docPath stringByAppendingPathComponent:@"girl.txt"];
NSArray *arr = @[@"你", @"好", @"2020", @"年"];
[arr writeToFile:filePath atomically:YES];
NSLog(@"%@", filePath);

2.3.4 从文件中读取数组的方法

NSArray *resultArr = [NSArray arrayWithContentsOfFile:filePath];
NSLog(@"%@", resultArr);

2.3.5 字典写入文件

NSString *dicPath = [docPath stringByAppendingString:@"dic.txt"];
NSDictionary *dic = @{@"你" : @"好", @"我" : @"2020"};
[dic writeToFile:dicPath atomically:YES];
NSLog(@"%@", dicPath);

2.3.6 从文件中读取字典

NSDictionary *resultDic = [NSDictionary dictionaryWithContentsOfFile:dicPath];
NSLog(@"%@", resultDic);

2.3.7 NSData写入文件

NSString *dataPath = [docPath stringByAppendingPathComponent:@"icon"];
// 得到一个UIImage对象
UIImage *image = [UIImage imageNamed:@"icon.jpg"];
// 将UIImage对象转换成NSData对象
NSData *data = UIImageJPEGRepresentation(image, 1.0);
[data writeToFile:dataPath atomically:YES];
NSLog(@"%@", dataPath);

2.3.8 从文件中读取NSData文件

NSData *resultData = [NSData dataWithContentsOfFile:dataPath];
// 将得到的NSData数据转换为原有的图片对象
UIImage *resultImage = [UIImage imageWithData:resultData];
// 显示图片
UIImageView *imageView = [[UIImageView alloc ] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
imageView.image = resultImage;
[self.view addSubview:imageView];

2.4 文件管理器与文件对接器

  • 文件管理器(NSFileManager):此类主要是对文件进行的操作(创建/从删除/改名等)以及文件信息的获取
  • 文件连接器(NSFileHandle):此类主要是对内容进行读取和写入操作
    常用的方法:
+ (id)fileHandleForReadingAtPath:(NSString *)path  //打开一个文件准备读取     

+ (id)fileHandleForWritingAtPath:(NSString *)path  //打开一个文件准备写入   

+ (id)fileHandleForUpdatingAtPath:(NSString *)path  //打开一个文件准备更新  

-  (NSData *)availableData; //从设备或通道返回可用的数据            

-  (NSData *)readDataToEndOfFile; //从当前的节点读取到文件的末尾               

-  (NSData *)readDataOfLength:(NSUInteger)length; //从当前节点开始读取指定的长度数据                           

-  (void)writeData:(NSData *)data; //写入数据         

-  (unsigned long long)offsetInFile;  //获取当前文件的偏移量            

-  (void)seekToFileOffset:(unsigned long long)offset; //跳到指定文件的偏移量     

-  (unsigned long long)seekToEndOfFile; //跳到文件末尾        

-  (void)truncateFileAtOffset:(unsigned long long)offset; //将文件的长度设为offset字节

-  (void)closeFile;  关闭文件

2.4.1 文件管理器的使用

2.4.1.1 创建文件夹
	// 1.找到Caches的路径
    NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
    // 2.获取创建的文件夹的路径
    NSString *directoryPath = [cachePath stringByAppendingPathComponent:@"downloadImages"];
    // 3.创建文件夹需要一个文件管理对象(单例)
    NSFileManager *fileManager = [NSFileManager defaultManager];
    // 4.创建文件夹
    [fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:nil];
    
    NSLog(@"%@", directoryPath);
2.4.1.2 创建文件以及获取文件信息
    // 1.得到Documents的路径
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    // 2.创建一个文件路径
    NSString *filePath = [docPath stringByAppendingPathComponent:@"qiuxiang.txt"];
    // 3.创建文件首先需要一个文件管理对象
    NSFileManager *fileManager = [NSFileManager defaultManager];
    // 4.创建文件
    [fileManager createFileAtPath:filePath contents:[@"你好" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
    
    NSLog(@"%@", filePath);
    
    // 获取默认文件或者某个文件夹的大小
    NSDictionary *dic = [fileManager attributesOfItemAtPath:filePath error:nil];
    NSLog(@"%@", dic);
    NSNumber *number = [dic objectForKey:NSFileSize];
    NSLog(@"%@", number);
2.4.1.3 文件移动
/**
    在Documents文件夹下,创建一个文件夹(path),在该文件夹下创建一个文件(test.txt),将一个图片对象存入到该文件中,然后在Caches文件夹下创建一个文件夹名为"testDirectroy",将test.txt文件移动到这个文件夹下.
 */
// 创建文件夹
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *dirPath = [docPath stringByAppendingPathComponent:@"path"];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:nil];

// 创建文件
NSString *filePath = [dirPath stringByAppendingPathComponent:@"test.txt"];
[fileManager createFileAtPath:filePath contents:nil attributes:nil];

// 将图片对象存入到该文件中
UIImage *image = [UIImage imageNamed:@"icon.jpg"];
NSData *data = UIImageJPEGRepresentation(image, 1.0);
[data writeToFile:filePath atomically:YES];
NSLog(@"%@", filePath);

// 移动文件
NSString *desPath = [docPath stringByAppendingPathComponent:@"test.txt"];
[fileManager moveItemAtPath:filePath toPath:desPath error:nil];

2.4.2 文件对接器的使用

可以利用文件对接器,实现对ios的txt文件的续写,而不是覆盖的问题

- (void)writefile:(NSString *)string
{
   // 得到Documents的路径
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

    NSString *filePath = [docPath stringByAppendingPathComponent:@"testfile.text"];

    NSFileManager *fileManager = [NSFileManager defaultManager];

    if(![fileManager fileExistsAtPath:filePath]) //如果不存在
    {
        NSString *str = @"姓  名/手  机  号/邮  件";
        [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];

    }
	// 打开文件准备更新
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
    // 移到文件末尾
    [fileHandle seekToEndOfFile]; 

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *datestr = [dateFormatter stringFromDate:[NSDate date]];

    NSString *str = [NSString stringWithFormat:@"\n%@\n%@",datestr,string];

    NSData* stringData  = [str dataUsingEncoding:NSUTF8StringEncoding];
	// 写入数据
    [fileHandle writeData:stringData];
	// 关闭文件
    [fileHandle closeFile];
}

2.5 iOS存储Excel格式

首先要下载excel提供的开发包,下载地址:Excel开发包,然后在工程中修改BitcodeNoOther Linker Flags设置为:-lstdc++。如下:
在这里插入图片描述

2.5.1 Excel的SDK调用demo

由于demo不算复杂,就没有上传至GitHub,复制下来可以直接运行。

#import "ViewController.h"
#import <LibXL/LibXL.h>

@interface ViewController () <UIDocumentInteractionControllerDelegate>

@property (nonatomic, strong) NSArray *nameArray;
@property (nonatomic, strong) NSArray *sexArray;
@property (nonatomic, strong) NSArray *ageArray;
@property (nonatomic, strong) NSArray *cityArray;
@property (nonatomic, strong) NSArray *schoolArray;
@property (nonatomic, strong) NSArray *phoneArray;
@property (nonatomic, strong) UIDocumentInteractionController *documentIc;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.nameArray = @[@"张三", @"李四", @"王五", @"赵六", @"孙七"];
    self.sexArray = @[@"男", @"女", @"男", @"女", @"男"];
    self.ageArray = @[@"19", @"18", @"20", @"20", @"19"];
    self.cityArray = @[@"北京", @"北京", @"北京", @"深dq圳", @"杭sdq州"];
    self.phoneArray = @[@"21.351231245", @"\n", @"0.9934174", @"1351351.124125", @"13777777777"];
    
    UIButton *exportBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    exportBtn.frame = CGRectMake(100, 100, 200, 30);
    [exportBtn setTitle:@"导出excel表格数据" forState: UIControlStateNormal];
    [exportBtn setTitleColor:[UIColor brownColor] forState:UIControlStateNormal];
    [exportBtn addTarget:self action:@selector(exportBtnDidClick) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:exportBtn];
}

- (void)exportBtnDidClick {
    
    // 创建excel文件,表格的格式是xls,如果要创建xlsx表格,需要用xlCreateXMLBook()创建
    BookHandle book = xlCreateBook();
    
    // 创建sheet表格
    SheetHandle sheet = xlBookAddSheet(book, "Sheet1", NULL);
    
    /**
     *  设置表格的列宽
     *  参数1:数据要写入的表格
     *  参数2:从哪一列开始
     *  参数3:到哪一列结束
     *  参数4:具体的列宽
     *  参数5:数据要转换的格式,类型是FormatHandle,不清楚怎么定义的话可以直接写0,使用默认的
     *  参数6:隐藏属性,true
     */
    xlSheetSetCol(sheet, 4, 4, 15, 0, true);
    
    /**
     *  第一行的标题数据
     *  参数1:数据要写入的表格
     *  参数2:写入到哪一行
     *  参数3:写入到哪一列
     *  参数4:要写入的具体内容,注意是C字符串
     *  参数5:数据要转换的格式,类型是FormatHandle,不清楚怎么定义的话可以直接写0,使用默认的
     */
    xlSheetWriteStr(sheet, 1, 0, "姓名", 0);
    xlSheetWriteStr(sheet, 1, 1, "性别", 0);
    xlSheetWriteStr(sheet, 1, 2, "年龄", 0);
    xlSheetWriteStr(sheet, 1, 3, "城市", 0);
    xlSheetWriteStr(sheet, 1, 4, "电话", 0);
    
    // 从第二行开始写入数据,先把OC字符串转成C字符串
    for (int i = 0; i < self.nameArray.count; i++) {
        const char *name = [self.nameArray[i] cStringUsingEncoding:NSUTF8StringEncoding];
        xlSheetWriteStr(sheet, i + 2, 0, name, 0);
    }
    
    for (int i = 0; i < self.sexArray.count; i++) {
        const char *sex = [self.sexArray[i] cStringUsingEncoding:NSUTF8StringEncoding];
        xlSheetWriteStr(sheet, i + 2, 1, sex, 0);
    }
    
    for (int i = 0; i < self.ageArray.count; i++) {
        const char *age = [self.ageArray[i] cStringUsingEncoding:NSUTF8StringEncoding];
        xlSheetWriteStr(sheet, i + 2, 2, age, 0);
    }
    
    for (int i = 0; i < self.cityArray.count; i++) {
        const char *city = [self.cityArray[i] cStringUsingEncoding:NSUTF8StringEncoding];
        xlSheetWriteStr(sheet, i + 2, 3, city, 0);
    }
    
    for (int i = 0; i < self.phoneArray.count; i++) {
        const char *phone = [self.phoneArray[i] cStringUsingEncoding:NSUTF8StringEncoding];
        xlSheetWriteStr(sheet, i + 2, 4, phone, 0);
    }
    
    // 先写入沙盒
    NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)firstObject];
    NSString *fileName = [@"student" stringByAppendingString:@".xls"];
    NSString *filePath = [documentPath stringByAppendingPathComponent:fileName];
    
    // 保存表格
    xlBookSave(book, [filePath UTF8String]);
    
    // 最后要release表格
    xlBookRelease(book);
    
    // 调用safari分享功能将文件分享出去
    UIDocumentInteractionController *documentIc = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
    
    // 记得要强引用UIDocumentInteractionController,否则控制器释放后再次点击分享程序会崩溃
    self.documentIc = documentIc;
    
    // 如果需要其他safari分享的更多交互,可以设置代理
    documentIc.delegate = self;
    
    // 设置分享显示的矩形框
    CGRect rect = CGRectMake(0, 0, 300, 300);
    [documentIc presentOpenInMenuFromRect:rect inView:self.view animated:YES];
    [documentIc presentPreviewAnimated:YES];
}


@end

三、UIDocumentInteractionController的使用

UIDocumentInteractionController是OC语言的一个类,但是他并不是一个controller,而是一个继承自NSObject类

3.1 主要作用

  1. 预览类似pdf、doc、ppt等类型文件的类。
  2. 可以将用户接收到的文件分享到用户手机上的其他App中。

3.2 使用方法

3.2.1 创建一个UIDocumentInteractionController类的属性:

@property (nonatomic,strong)UIDocumentInteractionController * document;

3.2.2 遵循UIDocumentInteractionControllerDelegate

@interface ViewController () <UIDocumentInteractionControllerDelegate>

3.2.3 调用safari分享功能将文件分享出去

// 调用safari分享功能将文件分享出去
UIDocumentInteractionController *documentIc = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];

// 记得要强引用UIDocumentInteractionController,否则控制器释放后再次点击分享程序会崩溃
self.documentIc = documentIc;

// 如果需要其他safari分享的更多交互,可以设置代理
documentIc.delegate = self;

// 设置分享显示的矩形框
CGRect rect = CGRectMake(0, 0, 300, 300);
[documentIc presentOpenInMenuFromRect:rect inView:self.view animated:YES];
[documentIc presentPreviewAnimated:YES];

3.2.4 判断手机中有没有应用可以打开该文件并打开分享界面

// 用户预览文件
//BOOL canOpen =   [_document presentPreviewAnimated:YES];
// 用户不预览文件直接分享
    BOOL canOpen = [self.document presentOpenInMenuFromRect:self.view.bounds inView:self.view animated:YES];
    if(!canOpen) {
   NSLog(@"沒有程序可以打开选中的文件");
    }

四、补充内容

作为一次的补充,主要想到如何能够像Python的函数参数*args一样,实现多参数,不定参数的传入,在Object-C中又是一种什么格式。
一个可变参数函数是指一个函数拥有不定的参数,即为一个函数可以接受多个参数。

4.1 不定参数

4.1.1 举个例子

// 类声明

@interface TestClass : NSObject
+ (void)print:(NSString *)firstArg, ... NS_REQUIRES_NIL_TERMINATION;
@end
// 类实现 
@implementation TestClass

+ (void)print:(NSString *)firstArg, ... NS_REQUIRES_NIL_TERMINATION {
    if (firstArg) {
        // 取出第一个参数
        NSLog(@"%@", firstArg);
        // 定义一个指向个数可变的参数列表指针;
        va_list args;
        // 用于存放取出的参数
        NSString *arg;
        // 初始化变量刚定义的va_list变量,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数
        va_start(args, firstArg);
        // 遍历全部参数 va_arg返回可变的参数(a_arg的第二个参数是你要返回的参数的类型)
        while ((arg = va_arg(args, NSString *))) {
            NSLog(@"%@", arg);
        }
        // 清空参数列表,并置参数指针args无效
        va_end(args);
    }
}

@end
  • va_list:用来保存宏 va_start 、va_arg 和 va_end 所需信息的一种类型。为了访问变长参数列表中的参数,必须声明 va_list 类型的一个对象。
  • va_start:访问变长参数列表中的参数之前使用的宏,它初始化用va_list声明的对象,初始化结果供宏va_arg和va_end使用;
  • va_arg:展开成一个表达式的宏,该表达式具有变长参数列表中下一个参数的值和类型。每次调用 va_arg 都会修改,用 va_list 声明的对象从而使该对象指向参数列表中的下一个参数。
  • va_end:该宏使程序能够从变长参数列表用宏 va_start 引用的函数中正常返回。
  • NS_REQUIRES_NIL_TERMINATION :是一个宏,用于编译时非nil结尾的检查。

4.1.2 注意事项

使用的时候,可变参后最后面加一个nil值,这样是代表结束的意思。就像UIAlertView初始化的那样,它一开始不知道你有多少个Button,你可以自由地往里加Button,最后也是由一个nil结束,所以两个是一样的道理。
例如上边的调用就是:

[TestClass print:@"1", @"2", @"3", nil];

4.1.3 注意点

  1. 当我们要创建一个可变参数函数时,必须把省略号( … )放到参数列表后面,同时也只能拥有一个这样的格式,因为我们不能定义两个可变的参数。
  2. 当我们需要读取 可变参数列表 时,必须先指定一个变数 va_list ,然后使用宏 va_start 、va_arg、va_end 来获取。

4.2 图片的批量读取

提供一个很简便的图片批量读取的方法

4.2.1 准备一个装满图片的bundle

制作方法很简单,将图片放到一个文件夹内,然后将文件夹重命名为xxx.bundle
在这里插入图片描述
点击使用.bundle 后就制作了一个简单的bundle。

4.2.2 图片读取

这里呢主要是从bundle中读取图片,然后将图片存到一个可变数组内即可,然后再工程中使用这个数组就可以了。也可以只存路径名,这个看个人需求。

- (void)getImagePath
{
    self.photoArray = [[NSMutableArray alloc] init];
    //得到文件的路径
    NSString *path = [[NSBundle mainBundle] pathForResource:@"pic" ofType:@"bundle"];
    //创建文件管理器
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtPath:path];
    
    while((path = [enumerator nextObject]) != nil) {
        //把得到的图片添加到数组中
        NSLog(@"地址:%@", path);
        NSString *imagePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:[NSString stringWithFormat:@"pic.bundle/%@", path]];
        UIImage *image = [UIImage imageNamed:imagePath];
        
        // 图片保存的功能,这样可以读取一个保存一个图片
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
        [self.photoArray addObject:image];
    }
    NSLog(@"完成图片读取");
}

4.2.3 举一个图片存入沙盒以及从沙盒读取的例子

[UIImageJPEGRepresentation(image, 100)writeToFile: filePath atomically:YES];
// 也可以是png

五、demo工程

这是图片批量读取的demo,包含了分享、保存功能
数据的保存与导出以及不定参数demo工程点这里,🤚

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值