/**
 *  Save and load model into miniprogram file system
 *  https://developers.weixin.qq.com/miniprogram/dev/api/file/wx.getFileSystemManager.html
 */ import { io } from '@tensorflow/tfjs-core' ; import { getModelArtifactsInfoForJSON } from './model_artifacts' ; type StoragePaths = { info : string ; modelArtifactsWithoutWeights : string ; weightData : string ; } ; const PATH_SEPARATOR = '/' ; const PATH_PREFIX = 'tensorflowjs_models' ; const INFO_SUFFIX = 'info.json' ; const MODEL_SUFFIX = 'model_without_weight.json' ; const WEIGHT_DATA_SUFFIX = 'weight_data' ; function getUserDataPath ( ) { if ( ! wx . env ) { wx . env = { USER_DATA_PATH : 'http://usr' , } ; } return wx . env . USER_DATA_PATH ; } const MODEL_PATH = [ getUserDataPath ( ) , PATH_PREFIX ] . join ( PATH_SEPARATOR ) ; function getModelPaths ( prefix ) { return { info : [ MODEL_PATH , `${ prefix }_${ INFO_SUFFIX }` ] . join ( PATH_SEPARATOR ) , modelArtifactsWithoutWeights : [ MODEL_PATH , `${ prefix }_${ MODEL_SUFFIX }` ] . join ( PATH_SEPARATOR ) , weightData : [ MODEL_PATH , `${ prefix }_${ WEIGHT_DATA_SUFFIX }` ] . join ( PATH_SEPARATOR ) , } ; } param fsm param filePath function removeFile ( fsm , filePath ) { return new Promise ( ( resolve , reject ) => { fsm . unlink ( { filePath , success : ( res ) => { resolve ( res ) ; } , fail : ( res ) => { resolve ( res ) ; } } ) ; } ) ; } param fsm param filePath param encoding function readFile ( fsm , filePath , encoding ? ) { return new Promise ( ( resolve , reject ) => { fsm . readFile ( { filePath , encoding , success : ( res ) => { if ( res . data ) { resolve ( res . data ) ; return ; } reject ( new Error ( 'Empty File' ) ) ; } , fail : ( res ) => { reject ( new Error ( res . errMsg ) ) ; } } ) ; } ) ; } param fsm param filePath param data param encoding function writeFile ( fsm , filePath , data , encoding = 'binary' ) { return new Promise ( ( resolve , reject ) => { removeFile ( fsm , filePath ) . then ( ( ) => { fsm . writeFile ( { filePath , data , encoding , success : ( res ) => { resolve ( res ) ; } , fail : ( res ) => { reject ( new Error ( res . errMsg ) ) ; } } ) ; } ) ; } ) ; } param fsm param dirPath function mkdir ( fsm , dirPath ) { return new Promise ( ( resolve , reject ) => { fsm . access ( { path : dirPath , success : ( ) => { resolve ( ) ; } , fail : ( ) => { fsm . mkdir ( { dirPath , recursive : true , success : ( res ) => { resolve ( res ) ; } , fail : ( res ) => { reject ( new Error ( res . errMsg ) ) ; } } ) ; } } ) ; } ) ; } class FileStorageHandler implements io . IOHandler { protected readonly paths : StoragePaths ; constructor ( private prefix , private fsm ) { if ( prefix == null || ! prefix ) { throw new Error ( 'prefix must not be null, undefined or empty.' ) ; } this . paths = getModelPaths ( this . prefix ) ; } param modelArtifacts returns async save ( modelArtifacts ) { if ( modelArtifacts . modelTopology instanceof ArrayBuffer ) { throw new Error ( 'AsyncStorageHandler.save() does not support saving model topology ' + 'in binary format.' ) ; } else { const modelArtifactsInfo = getModelArtifactsInfoForJSON ( modelArtifacts ) ; const { weightData , ... modelArtifactsWithoutWeights } = modelArtifacts ; try { await mkdir ( this . fsm , MODEL_PATH ) ; await writeFile ( this . fsm , this . paths . info , JSON . stringify ( modelArtifactsInfo ) , 'utf-8' ) ; await writeFile ( this . fsm , this . paths . modelArtifactsWithoutWeights , JSON . stringify ( modelArtifactsWithoutWeights ) , 'utf-8' ) ; await writeFile ( this . fsm , this . paths . weightData , weightData ) ; return { modelArtifactsInfo } ; } catch ( err ) { await removeFile ( this . fsm , this . paths . info ) ; await removeFile ( this . fsm , this . paths . modelArtifactsWithoutWeights ) ; await removeFile ( this . fsm , this . paths . weightData ) ; throw new Error ( err ) ; } } } returns async load ( ) { const info = JSON . parse ( ( await readFile ( this . fsm , this . paths . info , 'utf-8' ) ) as string ) ; if ( info == null ) { throw new Error ( `In file storage, there is no model with name '${ this . prefix }'` ) ; } if ( info . modelTopologyType !== 'JSON' ) { throw new Error ( 'fileStorage does not support loading non-JSON model ' + 'topology yet.' ) ; } const modelArtifacts = JSON . parse ( ( await readFile ( this . fsm , this . paths . modelArtifactsWithoutWeights , 'utf-8' ) ) as string ) ; modelArtifacts . weightData = await readFile ( this . fsm , this . paths . weightData ) ; return modelArtifacts ; } } param prefix param fileManager returns export function fileStorageIO ( prefix , fileManager ) { return new FileStorageHandler ( prefix , fileManager ) ; }