30、鸿蒙Harmony Next开发:应用文件上传下载,压缩与解压
目录
应用文件上传下载
上传应用文件
下载网络资源文件至应用文件目录
下载网络资源文件至用户文件
下载文档类文件
下载音频类文件
下载图片或视频类文件
添加网络配置
HTTP拦截
压缩与解压
接口说明
开发步骤
环境准备
Zip文件的压缩与解压
已知大小缓冲区的压缩与解压
未知大小缓冲区的压缩与解压(zlib格式)
未知大小缓冲区的压缩与解压(gzip格式)
应用文件上传下载
应用可以将应用文件上传到网络服务器,也可以从网络服务器下载网络资源文件到本地应用文件目录。
上传应用文件
开发者可以使用上传下载模块(ohos.request)的上传接口将本地文件上传。文件上传过程使用系统服务代理完成,在api12中request.agent.create接口增加了设置代理地址参数,支持用户设置自定义代理地址。
当前上传应用文件功能。request.uploadFile方式仅支持上传应用缓存文件路径(cacheDir)下的文件,request.agent方式支持上传用户公共文件和应用缓存文件路径下的文件。
使用上传下载模块,需声明权限:ohos.permission.INTERNET。
以下示例代码演示两种将应用缓存文件路径下的文件上传至网络服务器的方式:
// 方式一:request.uploadFile
// pages/xxx.ets
import { common } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import { BusinessError, request } from '@kit.BasicServicesKit';@Entry
@Component
struct Index {build() {Row() {Column() {Button("上传").onClick(() => {// 获取应用文件路径// 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContextlet context = this.getUIContext().getHostContext() as common.UIAbilityContext;let cacheDir = context.cacheDir;// 新建一个本地应用文件let file = fs.openSync(cacheDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);fs.writeSync(file.fd, 'upload file test');fs.closeSync(file);// 上传任务配置项let files: Array<request.File> = [//uri前缀internal://cache 对应cacheDir目录{ filename: 'test.txt', name: 'test', uri: 'internal://cache/test.txt', type: 'txt' }]let data: Array<request.RequestData> = [{ name: 'name', value: 'value' }];let uploadConfig: request.UploadConfig = {url: 'https://xxx',header: {'key1':'value1','key2':'value2'},method: 'POST',files: files,data: data}// 将本地应用文件上传至网络服务器try {request.uploadFile(context, uploadConfig).then((uploadTask: request.UploadTask) => {uploadTask.on('complete', (taskStates: Array<request.TaskState>) => {for (let i = 0; i < taskStates.length; i++) {console.info(`upload complete taskState: ${JSON.stringify(taskStates[i])}`);}});}).catch((err: BusinessError) => {console.error(`Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);})} catch (error) {let err: BusinessError = error as BusinessError;console.error(`Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);}})}}}
}
// 方式二:request.agent
// pages/xxx.ets
import { common } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import { BusinessError, request } from '@kit.BasicServicesKit';@Entry
@Component
struct Index {build() {Row() {Column() {Button("上传").onClick(() => {// 获取应用文件路径// 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContextlet context = this.getUIContext().getHostContext() as common.UIAbilityContext;let cacheDir = context.cacheDir;// 新建一个本地应用文件let file = fs.openSync(cacheDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);fs.writeSync(file.fd, 'upload file test');fs.closeSync(file);let attachments: Array<request.agent.FormItem> = [{name: "test",value: [{filename: "test.txt",path: "./test.txt",},]}];let config: request.agent.Config = {action: request.agent.Action.UPLOAD,url: 'http://xxx',mode: request.agent.Mode.FOREGROUND,overwrite: true,method: "POST",headers: {'key1':'value1','key2':'value2'},data: attachments,saveas: "./"};request.agent.create(context, config).then((task: request.agent.Task) => {task.start((err: BusinessError) => {if (err) {console.error(`Failed to start the upload task, Code: ${err.code} message: ${err.message}`);return;}});task.on('progress', async(progress) => {console.warn(`/Request upload status ${progress.state}, uploaded ${progress.processed}`);})task.on('completed', async() => {console.warn(`/Request upload completed`);//该方法需用户管理任务生命周期,任务结束后调用remove释放task对象request.agent.remove(task.tid);})}).catch((err: BusinessError) => {console.error(`Failed to create a upload task, Code: ${err.code}, message: ${err.message}`);});})}}}
}
下载网络资源文件至应用文件目录
开发者可以使用上传下载模块(ohos.request)的下载接口将网络资源文件下载到应用文件目录。对已下载的网络资源文件,开发者可以使用基础文件IO接口(ohos.file.fs)对其进行访问,使用方式与应用文件访问一致。文件下载过程使用系统服务代理完成,在api12中request.agent.create接口增加了设置代理地址参数,支持用户设置自定义代理地址。
当前网络资源文件仅支持下载至应用文件目录。
使用上传下载模块,需声明权限:ohos.permission.INTERNET。
以下示例代码演示两种将网络资源文件下载到应用文件目录的方式:
// 方式一:request.downloadFile
// pages/xxx.ets
// 将网络资源文件下载到应用文件目录并读取一段内容
import { common } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import { BusinessError, request } from '@kit.BasicServicesKit';
import { buffer } from '@kit.ArkTS';@Entry
@Component
struct Index {build() {Row() {Column() {Button("下载").onClick(() => {// 获取应用文件路径// 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContextlet context = this.getUIContext().getHostContext() as common.UIAbilityContext;let filesDir = context.filesDir;try {request.downloadFile(context, {url: 'https://xxxx/xxxx.txt',filePath: filesDir + '/xxxx.txt'}).then((downloadTask: request.DownloadTask) => {downloadTask.on('complete', () => {console.info('download complete');let file = fs.openSync(filesDir + '/xxxx.txt', fs.OpenMode.READ_WRITE);let arrayBuffer = new ArrayBuffer(1024);let readLen = fs.readSync(file.fd, arrayBuffer);let buf = buffer.from(arrayBuffer, 0, readLen);console.info(`The content of file: ${buf.toString()}`);fs.closeSync(file);})}).catch((err: BusinessError) => {console.error(`Invoke downloadTask failed, code is ${err.code}, message is ${err.message}`);});} catch (error) {let err: BusinessError = error as BusinessError;console.error(`Invoke downloadFile failed, code is ${err.code}, message is ${err.message}`);}})}}}
}
// 方式二:request.agent
// pages/xxx.ets
// 将网络资源文件下载到应用文件目录并读取一段内容
import { common } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import { BusinessError, request } from '@kit.BasicServicesKit';
import { buffer } from '@kit.ArkTS';@Entry
@Component
struct Index {build() {Row() {Column() {Button("下载").onClick(() => {// 获取应用文件路径// 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContextlet context = this.getUIContext().getHostContext() as common.UIAbilityContext;let filesDir = context.filesDir;let config: request.agent.Config = {action: request.agent.Action.DOWNLOAD,url: 'https://xxxx/test.txt',saveas: 'xxxx.txt',gauge: true,overwrite: true,network: request.agent.Network.WIFI,};request.agent.create(context, config).then((task: request.agent.Task) => {task.start((err: BusinessError) => {if (err) {console.error(`Failed to start the download task, Code: ${err.code} message: ${err.message}`);return;}});task.on('progress', async (progress) => {console.warn(`/Request download status ${progress.state}, downloaded ${progress.processed}`);})task.on('completed', async () => {console.warn(`/Request download completed`);let file = fs.openSync(filesDir + '/xxxx.txt', fs.OpenMode.READ_WRITE);let arrayBuffer = new ArrayBuffer(1024);let readLen = fs.readSync(file.fd, arrayBuffer);let buf = buffer.from(arrayBuffer, 0, readLen);console.info(`The content of file: ${buf.toString()}`);fs.closeSync(file);//该方法需用户管理任务生命周期,任务结束后调用remove释放task对象request.agent.remove(task.tid);})}).catch((err: BusinessError) => {console.error(`Failed to create a download task, Code: ${err.code}, message: ${err.message}`);});})}}}
}
下载网络资源文件至用户文件
开发者可以使用上传下载模块(ohos.request)的request.agent下载接口将网络资源文件下载到用户文件。
下载文档类文件
开发者可以通过调用DocumentViewPicker的save()接口保存文件并获得用户文件的uri,将此uri作为Config的saveas字段值进行下载。
import { BusinessError, request } from '@kit.BasicServicesKit';
import { picker } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';@Entry
@Component
struct Index {build() {Row() {Column() {Button("下载文档").width("50%").margin({ top: 20 }).height(40).onClick(async () => {// 创建文件管理器选项实例。const documentSaveOptions = new picker.DocumentSaveOptions();// 保存文件名(可选)。 默认为空。documentSaveOptions.newFileNames = ["xxxx.txt"];// 保存文件类型['后缀类型描述|后缀类型'],选择所有文件:'所有文件(*.*)|.*'(可选),如果选择项存在多个后缀(最大限制100个过滤后缀),默认选择第一个。如果不传该参数,默认无过滤后缀。documentSaveOptions.fileSuffixChoices = ['文档|.txt', '.pdf'];let uri: string = '';// 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContextlet context = this.getUIContext().getHostContext() as common.UIAbilityContext;const documentViewPicker = new picker.DocumentViewPicker(context);await documentViewPicker.save(documentSaveOptions).then((documentSaveResult: Array<string>) => {uri = documentSaveResult[0];console.info('DocumentViewPicker.save to file succeed and uri is:' + uri);}).catch((err: BusinessError) => {console.error(`Invoke documentViewPicker.save failed, code is ${err.code}, message is ${err.message}`);})if (uri != '') {let config: request.agent.Config = {action: request.agent.Action.DOWNLOAD,url: 'https://xxxx/xxxx.txt',// saveas字段是DocumentViewPicker保存的文件的urisaveas: uri,gauge: true,// overwrite字段必须为trueoverwrite: true,network: request.agent.Network.WIFI,// mode字段必须为request.agent.Mode.FOREGROUNDmode: request.agent.Mode.FOREGROUND,};try {request.agent.create(context, config).then((task: request.agent.Task) => {task.start((err: BusinessError) => {if (err) {console.error(`Failed to start the download task, Code: ${err.code} message: ${err.message}`);return;}});task.on('progress', async (progress) => {console.warn(`Request download status ${progress.state}, downloaded ${progress.processed}`);})task.on('completed', async (progress) => {console.warn('Request download completed, ' + JSON.stringify(progress));// 该方法需用户管理任务生命周期,任务结束后调用remove释放task对象request.agent.remove(task.tid);})}).catch((err: BusinessError) => {console.error(`Failed to operate a download task, Code: ${err.code}, message: ${err.message}`);});} catch (err) {console.error(`Failed to create a download task, err: ${err}`);}}})}}}
}
下载音频类文件
开发者可以通过调用AudioViewPicker的save()接口保存文件并获得用户文件的uri,将此uri作为Config的saveas字段值进行下载。
import { BusinessError, request } from '@kit.BasicServicesKit';
import { picker } from '@kit.CoreFileKit';
import { common } from '@kit.AbilityKit';@Entry
@Component
struct Index {build() {Row() {Column() {Button("下载音频").width("50%").margin({ top: 20 }).height(40).onClick(async () => {// 创建文件管理器选项实例。const audioSaveOptions = new picker.AudioSaveOptions();// 保存文件名(可选)。 默认为空。audioSaveOptions.newFileNames = ['xxxx.mp3'];let uri: string = '';// 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContextlet context = this.getUIContext().getHostContext() as common.UIAbilityContext;const audioViewPicker = new picker.AudioViewPicker(context);await audioViewPicker.save(audioSaveOptions).then((audioSelectResult: Array<string>) => {uri = audioSelectResult[0];console.info('AudioViewPicker.save to file succeed and uri is:' + uri);}).catch((err: BusinessError) => {console.error(`Invoke audioViewPicker.save failed, code is ${err.code}, message is ${err.message}`);})if (uri != '') {let config: request.agent.Config = {action: request.agent.Action.DOWNLOAD,url: 'https://xxxx/xxxx.mp3',// saveas字段是AudioViewPicker保存的文件的urisaveas: uri,gauge: true,// overwrite字段必须为trueoverwrite: true,network: request.agent.Network.WIFI,// mode字段必须为request.agent.Mode.FOREGROUNDmode: request.agent.Mode.FOREGROUND,};try {request.agent.create(context, config).then((task: request.agent.Task) => {task.start((err: BusinessError) => {if (err) {console.error(`Failed to start the download task, Code: ${err.code} message: ${err.message}`);return;}});task.on('progress', async (progress) => {console.warn(`Request download status ${progress.state}, downloaded ${progress.processed}`);})task.on('completed', async (progress) => {console.warn('Request download completed, ' + JSON.stringify(progress));// 该方法需用户管理任务生命周期,任务结束后调用remove释放task对象request.agent.remove(task.tid);})}).catch((err: BusinessError) => {console.error(`Failed to operate a download task, Code: ${err.code}, message: ${err.message}`);});} catch (err) {console.error(`Failed to create a download task, err: ${err}`);}}})}}}
}
下载图片或视频类文件
开发者可以通过调用PhotoAccessHelper的createAsset()接口创建媒体文件并获得用户文件的uri,将此uri作为Config的saveas字段值进行下载。
需要权限:ohos.permission.WRITE_IMAGEVIDEO
权限ohos.permission.WRITE_IMAGEVIDEO是system_basic级别的受限开放权限,normal等级的应用需要将自身的APL等级声明为system_basic及以上。授权方式为user_grant,需要向用户申请授权。
import { BusinessError, request } from '@kit.BasicServicesKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';import { bundleManager } from '@kit.AbilityKit';
import { abilityAccessCtrl, Context, PermissionRequestResult, common } from '@kit.AbilityKit';@Entry
@Component
struct Index {build() {Row() {Column() {Button("下载图片").width("50%").margin({ top: 20 }).height(40).onClick(async () => {let bundleFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION |bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_METADATA;// 获取应用程序的accessTokenID。let tokenID = -1;try {await bundleManager.getBundleInfoForSelf(bundleFlags).then((data) => {console.info(`/Request getBundleInfoForSelf successfully. Data: ${JSON.stringify(data)}`);tokenID = data.appInfo.accessTokenId;}).catch((err: BusinessError) => {console.error(`GetBundleInfoForSelf failed. Cause: ${err.message}`);});} catch (err) {let message = (err as BusinessError).message;console.error('GetBundleInfoForSelf failed: %{public}s', message);}let context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();let grant = true;// 校验应用是否授予权限。使用Promise异步回调。await atManager.checkAccessToken(tokenID, 'ohos.permission.WRITE_IMAGEVIDEO').then((data: abilityAccessCtrl.GrantStatus) => {console.log(`/Request checkAccessToken success, data->${JSON.stringify(data)}`);if (data != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {grant = false;}}).catch((err: BusinessError) => {console.error(`GheckAccessToken fail, err->${JSON.stringify(err)}`);});if (!grant) {// 用于UIAbility拉起弹框请求用户授权。使用callback异步回调。await atManager.requestPermissionsFromUser(context, ['ohos.permission.WRITE_IMAGEVIDEO']).then((data: PermissionRequestResult) => {console.info('/Request grant:' + JSON.stringify(data));console.info('/Request grant permissions:' + data.permissions);console.info('/Request grant authResults:' + data.authResults);console.info('/Request grant dialogShownResults:' + data.dialogShownResults);}).catch((err: BusinessError) => {console.error('Grant error:' + JSON.stringify(err));});}try {let photoType: photoAccessHelper.PhotoType = photoAccessHelper.PhotoType.IMAGE;let extension: string = 'jpg';let options: photoAccessHelper.CreateOptions = {title: 'xxxx'}// 获取相册管理模块的实例,用于访问和修改相册中的媒体文件。let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);// 指定文件类型、后缀和创建选项,创建图片或视频资源,以Promise方式返回结果。let uri: string = await phAccessHelper.createAsset(photoType, extension, options);console.info('/Request createAsset uri' + uri);console.info('/Request createAsset successfully');let config: request.agent.Config = {action: request.agent.Action.DOWNLOAD,url: 'https://xxxx/xxxx.jpg',// saveas字段是PhotoAccessHelper保存的文件的urisaveas: uri,gauge: true,// overwrite字段必须为trueoverwrite: true,network: request.agent.Network.WIFI,// mode字段必须为request.agent.Mode.FOREGROUNDmode: request.agent.Mode.FOREGROUND,};request.agent.create(context, config).then((task: request.agent.Task) => {task.start((err: BusinessError) => {if (err) {console.error(`Failed to start the download task, Code: ${err.code} message: ${err.message}`);return;}});task.on('progress', async (progress) => {console.warn(`Request download status ${progress.state}, downloaded ${progress.processed}`);})task.on('completed', async (progress) => {console.warn('Request download completed, ' + JSON.stringify(progress));//该方法需用户管理任务生命周期,任务结束后调用remove释放task对象request.agent.remove(task.tid);})}).catch((err: BusinessError) => {console.error(`Failed to operate a download task, Code: ${err.code}, message: ${err.message}`);});} catch (err) {console.error(`Failed to create a download task, err: ${err}`);}})}}}
}
添加网络配置
HTTP拦截
开发者可以通过设置配置文件实现HTTP拦截功能,上传下载模块在应用配置禁用HTTP后,无法创建明文HTTP传输的上传下载任务。配置文件在APP中的路径是:src/main/resources/base/profile/network_config.json。请参考网络管理模块配置文件配置参数
参考配置文件如下:
{"network-security-config": {"base-config": {"cleartextTrafficPermitted": true, "trust-anchors": [{"certificates": "/etc/security/certificates"}]},"domain-config": [{"cleartextTrafficPermitted": true,"domains": [{"include-subdomains": true,"name": "*.example.com"}],}]}
}
压缩与解压
接口说明
以下是示例中使用的主要接口,更多接口及使用方式请见接口文档。
接口名 | 接口描述 |
---|---|
compressFile(inFile: string, outFile: string, options: Options): Promise<void> | 压缩文件。 |
decompressFile(inFile: string, outFile: string, options?: Options): Promise<void> | 解压文件。 |
compress(dest: ArrayBuffer, source: ArrayBuffer, sourceLen?: number): Promise<ZipOutputInfo> | 将源缓冲区压缩到目标缓冲区。 |
compressBound(sourceLen: number): Promise<number> | 计算返回压缩大小的上限。 |
uncompress(dest:ArrayBuffer, source: ArrayBuffer, sourceLen?: number): Promise<ZipOutputInfo> | 将压缩后的数据解压缩为原始的未压缩形式。 |
deflate(strm: ZStream, flush: CompressFlushMode): Promise<ReturnStatus> | 压缩数据。 |
inflate(strm: ZStream, flush: CompressFlushMode): Promise<ReturnStatus> | 解压数据。 |
开发步骤
环境准备
在应用沙箱目录下创建一个测试文件data.txt,并写入测试数据。示例代码如下。
import { fileIo as fs} from '@kit.CoreFileKit';
import { BusinessError, zlib } from '@kit.BasicServicesKit';@Entry
@Component
struct Index {@State dataSize: number = 0;build() {Row() {Column() {// 在应用沙箱目录下创建文件data.txt,并写入测试数据Button('创建测试文件data.txt').onClick(() => {let path = this.getUIContext()?.getHostContext()?.filesDir;// 创建文件data.txtlet inFile = fs.openSync(path + '/data.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);// 写入测试数据for (let index = 0; index < 100; index++) {fs.writeSync(inFile.fd, index + ': hello world, hello world, hello world, hello world, hello world.\n');}// 获取测试数据原始大小,并保存到dataSize中let stat = fs.statSync(inFile.path);this.dataSize = stat.size;console.info('dataSize: ' + this.dataSize);// 关闭文件fs.closeSync(inFile);})}}.height('100%').width('100%')}
}
Zip文件的压缩与解压
采用接口zlib.compressFile()将文件data.txt压缩并归档到data.zip中,采用接口zlib.decompressFile()将data.zip解压到应用沙箱目录下,示例代码如下。
import { fileIo as fs} from '@kit.CoreFileKit';
import { BusinessError, zlib } from '@kit.BasicServicesKit';@Entry
@Component
struct Index {build() {Row() {// 示例一:将测试文件data.txt压缩并归档到data.zip中。Button('compressFile').onClick(() => {let path = this.getUIContext()?.getHostContext()?.filesDir;let inFile = path + '/data.txt';let outFile = path + '/data.zip';let options: zlib.Options = {};zlib.compressFile(inFile, outFile, options).then((data: void) => {console.info('compressFile success, data: ' + JSON.stringify(data));}).catch((errData: BusinessError) => {console.error(`compressFile errCode: ${errData.code}, message: ${errData.message}`);})})// 示例二:将data.zip文件解压到应用沙箱目录下。Button('decompressFile').onClick(() => {let path = this.getUIContext()?.getHostContext()?.filesDir;let inFile = path + '/data.zip';let outFile = path;let options: zlib.Options = {};zlib.decompressFile(inFile, outFile, options).then((data: void) => {console.info('decompressFile success, data: ' + JSON.stringify(data));}).catch((errData: BusinessError) => {console.error(`decompressFile errCode: ${errData.code}, message: ${errData.message}`);})})}.height('100%').width('100%')}
}
已知大小缓冲区的压缩与解压
针对一个已知大小的缓冲区中的数据,使用接口compress()将其压缩到一个目的缓冲区中,使用接口compressBound()计算压缩目的缓冲区大小的上限值,使用接口uncompress()对存储压缩数据的缓冲区进行解压。由于解压时无法获取解压后原始数据的大小,为了确认解压后目的缓冲区的大小,需要在压缩前获取原始数据的大小并保存,示例代码如下。
import { fileIo as fs} from '@kit.CoreFileKit';
import { BusinessError, zlib } from '@kit.BasicServicesKit';@Entry
@Component
struct Index {@State dataSize: number = 0; //用于保存原始数据的大小build() {Row() {// 示例一:读取data.txt文件内容并存入一个缓冲区,调用compress接口压缩缓冲区中的数据到目标缓冲区,并将目标缓冲区的内容写入文件data.binButton('compress buffer').onClick(() => {let path = this.getUIContext()?.getHostContext()?.filesDir;let inFile = fs.openSync(path + '/data.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let outFile = fs.openSync(path + '/data.bin', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);// 读取data.txt文件的内容,并存入缓冲区inBuflet stat = fs.statSync(inFile.path);let inBuf = new ArrayBuffer(stat.size);let readLen = fs.readSync(inFile.fd, inBuf);console.info(`original size: ${stat.size}, read len: ${readLen}`);// 获取原始数据的大小,并保存this.dataSize = stat.size;// 创建一个压缩对象实例let zip = zlib.createZipSync();// 获取一个目标缓冲区的上限zip.compressBound(stat.size).then((data) => {console.info(`the max dest buf len is ${data}`);// 目标缓冲区outBuflet outBuf = new ArrayBuffer(data);// 将inBuf中的数据压缩到outBuf中zip.compress(outBuf, inBuf, readLen).then((zipOutInfo) => {console.info(`compress success, status ${zipOutInfo.status}, destLen ${zipOutInfo.destLen}`);// 将outBuf中的数据写入到data.bin文件let writeLen = fs.writeSync(outFile.fd, outBuf, { length: zipOutInfo.destLen });console.info(`write destBuf to data.bin, writeLen ${writeLen}`);// 关闭文件fs.closeSync(inFile.fd);fs.closeSync(outFile.fd);}).catch((errData: BusinessError) => {console.error(`errData is errCode:${errData.code} message:${errData.message}`);})}).catch((errData: BusinessError) => {console.error(`errData is errCode:${errData.code} message:${errData.message}`);})})// 示例二:读取data.bin文件中的压缩数据并存入一个缓冲区,调用uncompress接口将缓冲区中的数据解压到目标缓冲区,并将目标缓冲区的内容写入文件data.txtButton('uncompress buffer').onClick(() => {let path = this.getUIContext()?.getHostContext()?.filesDir;let inFile = fs.openSync(path + '/data.bin', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let outFile = fs.openSync(path + '/data.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);// 读取data.bin文件中的压缩数据,并存入缓冲区inBuflet stat = fs.statSync(inFile.path);let inBuf = new ArrayBuffer(stat.size);let readLen = fs.readSync(inFile.fd, inBuf);console.info(`compressed data size: ${stat.size}, read len: ${readLen}`);// 创建一个目标缓冲区,此处的dataSize是我们进行数据压缩前保存的数据的原始大小let outBuf = new ArrayBuffer(this.dataSize);console.info(`the dest buf size is ${this.dataSize}`);// 创建一个压缩对象实例let zip = zlib.createZipSync();// 将inBuf中的数据解压缩outBuf中zip.uncompress(outBuf, inBuf, readLen).then((zipOutInfo) => {console.info(`uncompress success, status ${zipOutInfo.status}, destLen ${zipOutInfo.destLen}`);// 将outBuf中的数据写入到data.txt文件let writeLen = fs.writeSync(outFile.fd, outBuf, { length: zipOutInfo.destLen });console.info(`write destBuf to data.txt, writeLen ${writeLen}`);// 关闭文件fs.closeSync(inFile.fd);fs.closeSync(outFile.fd);}).catch((errData: BusinessError) => {console.error(`errData is errCode:${errData.code} message:${errData.message}`);})})}.height('100%').width('100%')}
}
未知大小缓冲区的压缩与解压(zlib格式)
针对一个未知大小的缓冲区中的数据,使用接口deflate()将从一个原始输入流中读取的数据进行压缩,使用接口inflate()将从一个压缩输入流中读取的数据进行解压,示例代码如下。
import { fileIo as fs} from '@kit.CoreFileKit';
import { BusinessError, zlib } from '@kit.BasicServicesKit';@Entry
@Component
struct Index {build() {Row() {// 示例一:从文件中不断读取数据进行压缩Button('deflateFile').onClick(() => {let path = this.getUIContext()?.getHostContext()?.filesDir;let inFile = fs.openSync(path + '/data.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let outFile = fs.openSync(path + '/data.bin', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);deflateFile(inFile, outFile).then(() => {console.info('deflateFile success');fs.closeSync(inFile.fd);fs.closeSync(outFile.fd);})})// 示例二:从文件中不断读取压缩数据进行解压Button('inflateFile').onClick(() => {let path = this.getUIContext()?.getHostContext()?.filesDir;let inFile = fs.openSync(path + '/data.bin', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let outFile = fs.openSync(path + '/data.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);inflateFile(inFile, outFile).then(() => {console.info('deflateFile success');fs.closeSync(inFile.fd);fs.closeSync(outFile.fd);})})}.height('100%').width('100%')}
}// 从一个文件中,不断的读入数据,进行压缩,并写入到另一个文件中
async function deflateFile(src: fs.File, dest: fs.File) {let flush = zlib.CompressFlushMode.NO_FLUSH;let strm: zlib.ZStream = {}; //初始化一个压缩流const BUFLEN = 4096;let inBuf = new ArrayBuffer(BUFLEN); // 初始化一个输入缓冲区let outBuf = new ArrayBuffer(BUFLEN); // 初始化一个输出缓冲区// 创建一个压缩对象实例let zip = zlib.createZipSync();// 初始化流的状态let initStatus = zip.deflateInit(strm, zlib.CompressLevel.COMPRESS_LEVEL_BEST_SPEED);console.info('deflateInit ret: ' + (await initStatus).valueOf());do {// 从文件中读取数据到缓冲区let readLen = fs.readSync(src.fd, inBuf);console.info("readSync readLen: " + readLen);flush = readLen == 0 ? zlib.CompressFlushMode.FINISH : zlib.CompressFlushMode.NO_FLUSH;// 设置输入缓冲区strm.availableIn = readLen;strm.nextIn = inBuf;do {// 设置输出缓冲区strm.availableOut = BUFLEN;strm.nextOut = outBuf;try {// 压缩输入缓冲区中数据到输出缓冲区let deflateStatus = zip.deflate(strm, flush);console.info('deflate ret: ' + (await deflateStatus).valueOf());// 更新流的状态let innerStrm = zip.getZStream();strm.availableIn = (await innerStrm).availableIn;strm.nextIn = (await innerStrm).nextIn;strm.availableOut = (await innerStrm).availableOut;strm.nextOut = (await innerStrm).nextOut;strm.totalIn = (await innerStrm).totalIn;strm.totalOut = (await innerStrm).totalOut;if (strm.availableOut != undefined) {// 将已完成压缩的数据,写入到输出文件中let have = BUFLEN - strm.availableOut;let writeLen = fs.writeSync(dest.fd, outBuf, { length: have });console.info(`writeSync writeLen: ${writeLen}`);}} catch (err) {console.error('deflate err: ' + JSON.stringify(err));}} while (strm.availableOut == 0); // 循环压缩输入缓冲区中剩余的数据,直到全部完成压缩} while (flush != zlib.CompressFlushMode.FINISH); // 循环从文件中读取数据,直到数据全部读取// 释放资源zip.deflateEnd(strm);
}// 从一个文件中,不断的读入已压缩的数据,进行解压,并写入到另一个文件中
async function inflateFile(src: fs.File, dest: fs.File) {let status: zlib.ReturnStatus = zlib.ReturnStatus.OK;let strm: zlib.ZStream = {}; //初始化一个压缩流const BUFLEN = 4096;let inBuf = new ArrayBuffer(BUFLEN); // 初始化一个输入缓冲区let outBuf = new ArrayBuffer(BUFLEN); // 初始化一个输出缓冲区// 创建一个压缩对象实例let zip = zlib.createZipSync();// 初始化流的状态let initStatus = zip.inflateInit(strm);console.info('inflateInit ret: ' + (await initStatus).valueOf());do {// 从文件中读取已压缩的数据到缓冲区let readLen = fs.readSync(src.fd, inBuf);console.info("readSync readLen: " + readLen);if (readLen == 0) {break;}// 设置输入缓冲区strm.availableIn = readLen;strm.nextIn = inBuf;do {// 设置输出缓冲区strm.availableOut = BUFLEN;strm.nextOut = outBuf;try {// 解压输入缓冲区中数据到输出缓冲区let inflateStatus = zip.inflate(strm, zlib.CompressFlushMode.NO_FLUSH);console.info('inflate ret: ' + (await inflateStatus).valueOf());// 更新流的状态let innerStrm = zip.getZStream();strm.availableIn = (await innerStrm).availableIn;strm.nextIn = (await innerStrm).nextIn;strm.availableOut = (await innerStrm).availableOut;strm.nextOut = (await innerStrm).nextOut;strm.totalIn = (await innerStrm).totalIn;strm.totalOut = (await innerStrm).totalOut;if (strm.availableOut != undefined) {// 将已完成解压的数据,写入到输出文件中let have = BUFLEN - strm.availableOut;let writeLen = fs.writeSync(dest.fd, outBuf, { length: have });console.info(`writeSync writeLen: ${writeLen}`);}} catch (err) {console.error('inflate err: ' + JSON.stringify(err));}} while (strm.availableOut == 0) // 循环解压输入缓冲区中剩余的数据,直到全部完成解压} while (status != zlib.ReturnStatus.STREAM_END.valueOf()) // 循环从文件中读取数据,直到数据全部读取// 释放资源zip.inflateEnd(strm);
}
未知大小缓冲区的压缩与解压(gzip格式)
采用gzip格式,针对一个未知大小的缓冲区中的数据,使用接口deflate()将从一个原始输入流中读取的数据进行压缩,使用接口inflate()将从一个压缩输入流中读取的数据进行解压,示例代码如下。
import { fileIo as fs} from '@kit.CoreFileKit';
import { BusinessError, zlib } from '@kit.BasicServicesKit';@Entry
@Component
struct Index {build() {Row() {// 示例一:从文件中不断读取数据进行压缩Button('deflateGzipFile').onClick(() => {let path = this.getUIContext()?.getHostContext()?.filesDir;let inFile = fs.openSync(path + '/data.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let outFile = fs.openSync(path + '/data.gz', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);deflateGzipFile(inFile, outFile).then(() => {console.info('deflateGzipFile success');fs.closeSync(inFile.fd);fs.closeSync(outFile.fd);})})// 示例二:从文件中不断读取压缩数据进行解压Button('inflateGzipFile').onClick(() => {let path = this.getUIContext()?.getHostContext()?.filesDir;let inFile = fs.openSync(path + '/data.gz', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let outFile = fs.openSync(path + '/data.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);inflateGzipFile(inFile, outFile).then(() => {console.info('deflateGzipFile success');fs.closeSync(inFile.fd);fs.closeSync(outFile.fd);})})}.height('100%').width('100%')}
}// 从一个文件中,不断的读入数据,进行压缩,并写入到另一个文件中
async function deflateGzipFile(src: fs.File, dest: fs.File) {let flush = zlib.CompressFlushMode.NO_FLUSH;let strm: zlib.ZStream = {}; //初始化一个压缩流const BUFLEN = 4096;let inBuf = new ArrayBuffer(BUFLEN); // 初始化一个输入缓冲区let outBuf = new ArrayBuffer(BUFLEN); // 初始化一个输出缓冲区// 创建一个压缩对象实例let zip = zlib.createZipSync();// 初始化流的状态,windowBits > 15时,启用gzip格式let windowBits = 15 + 16;let initStatus = zip.deflateInit2(strm, zlib.CompressLevel.COMPRESS_LEVEL_BEST_SPEED,zlib.CompressMethod.DEFLATED, windowBits, zlib.MemLevel.MEM_LEVEL_DEFAULT,zlib.CompressStrategy.COMPRESS_STRATEGY_DEFAULT_STRATEGY);console.info('deflateInit2 ret: ' + (await initStatus).valueOf());do {// 从文件中读取数据到缓冲区let readLen = fs.readSync(src.fd, inBuf);console.info("readSync readLen: " + readLen);flush = readLen == 0 ? zlib.CompressFlushMode.FINISH : zlib.CompressFlushMode.NO_FLUSH;// 设置输入缓冲区strm.availableIn = readLen;strm.nextIn = inBuf;do {// 设置输出缓冲区strm.availableOut = BUFLEN;strm.nextOut = outBuf;try {// 压缩输入缓冲区中数据到输出缓冲区let deflateStatus = zip.deflate(strm, flush);console.info('deflate ret: ' + (await deflateStatus).valueOf());// 更新流的状态let innerStrm = zip.getZStream();strm.availableIn = (await innerStrm).availableIn;strm.nextIn = (await innerStrm).nextIn;strm.availableOut = (await innerStrm).availableOut;strm.nextOut = (await innerStrm).nextOut;strm.totalIn = (await innerStrm).totalIn;strm.totalOut = (await innerStrm).totalOut;if (strm.availableOut != undefined) {// 将已完成压缩的数据,写入到输出文件中let have = BUFLEN - strm.availableOut;let writeLen = fs.writeSync(dest.fd, outBuf, { length: have });console.info(`writeSync writeLen: ${writeLen}`);}} catch (err) {console.error('deflate err: ' + JSON.stringify(err));}} while (strm.availableOut == 0); // 循环压缩输入缓冲区中剩余的数据,直到全部完成压缩} while (flush != zlib.CompressFlushMode.FINISH); // 循环从文件中读取数据,直到数据全部读取// 释放资源zip.deflateEnd(strm);
}// 从一个文件中,不断的读入已压缩的数据,进行解压,并写入到另一个文件中
async function inflateGzipFile(src: fs.File, dest: fs.File) {let status: zlib.ReturnStatus = zlib.ReturnStatus.OK;let strm: zlib.ZStream = {}; //初始化一个压缩流const BUFLEN = 4096;let inBuf = new ArrayBuffer(BUFLEN); // 初始化一个输入缓冲区let outBuf = new ArrayBuffer(BUFLEN); // 初始化一个输出缓冲区// 创建一个压缩对象实例let zip = zlib.createZipSync();// 初始化流的状态,windowBits > 15时,启用gzip格式let windowBits = 15 + 16;let initStatus = zip.inflateInit2(strm, windowBits);console.info('inflateInit2 ret: ' + (await initStatus).valueOf());do {// 从文件中读取已压缩的数据到缓冲区let readLen = fs.readSync(src.fd, inBuf);console.info("readSync readLen: " + readLen);if (readLen == 0) {break;}// 设置输入缓冲区strm.availableIn = readLen;strm.nextIn = inBuf;do {// 设置输出缓冲区strm.availableOut = BUFLEN;strm.nextOut = outBuf;try {// 解压输入缓冲区中数据到输出缓冲区let inflateStatus = zip.inflate(strm, zlib.CompressFlushMode.NO_FLUSH);console.info('inflate ret: ' + (await inflateStatus).valueOf());// 更新流的状态let innerStrm = zip.getZStream();strm.availableIn = (await innerStrm).availableIn;strm.nextIn = (await innerStrm).nextIn;strm.availableOut = (await innerStrm).availableOut;strm.nextOut = (await innerStrm).nextOut;strm.totalIn = (await innerStrm).totalIn;strm.totalOut = (await innerStrm).totalOut;if (strm.availableOut != undefined) {// 将已完成解压的数据,写入到输出文件中let have = BUFLEN - strm.availableOut;let writeLen = fs.writeSync(dest.fd, outBuf, { length: have });console.info(`writeSync writeLen: ${writeLen}`);}} catch (err) {console.error('inflate err: ' + JSON.stringify(err));}} while (strm.availableOut == 0) // 循环解压输入缓冲区中剩余的数据,直到全部完成解压} while (status != zlib.ReturnStatus.STREAM_END.valueOf()) // 循环从文件中读取数据,直到数据全部读取// 释放资源zip.inflateEnd(strm);
}