您的位置:首页 > 其它

软件项目技术点(19)——文件的保存和打开(解压缩)

2017-01-18 15:30 295 查看

AxeSlide软件项目梳理   canvas绘图系列知识点整理

保存文件

保存内容有哪些?

我们需要保存的内容信息

1)context.json 存储画布状态信息和所有元素的配置信息(这个文件在过程中生成)

2)插入的图片、音频、视频等资源

3)所用到的字体文件

4)每一帧的缩略图

将这些文件压缩到一个zip包里,我们的作品保存后的文件是dbk后缀格式的,其实就是一个压缩zip文件。

保存过程步骤解析

1)获取要保存的对象信息dtoCore,后面将其转换成字符串string后存储到文件里。

 2)将保存作品用到的资源整理到fileList中,fileList对象列表有rawPath(原路径)和target(目标路径)。

 3)利用模块zip-addon 将所有的file利用zip.exe压缩到目标路径

require("zip-addon") 可以从github上下载:https://github.com/liufangfang/zip-addon

下面是该模块的主要代码:

var fs = require('fs');
var cp = require('child_process');
var path = require('path');
var platform = process.platform;

function zipFiles(listfile){//利用zip.exe根据list.txt文件里的内容来处理压缩文件
var exeName = platform == 'win32' ? 'win/zip.exe' : 'mac/zip.out';
try{
cp.execFileSync(
path.join(__dirname, exeName),
[listfile],
{cwd: process.cwd()}
);
return '';
}
catch(e){
return String(e.stdout);
}
}

function createDbkFile2(fileList, targetZipPath){
var delimiter = platform == 'win32' ? "|" : ":";
var all = targetZipPath + '\n';
var len = fileList.length;
for(var i=0; i<len; i++){//拼接压缩文件内容字符串all
var rawPath = String(fileList[i].rawPath);
var targetPath = String(fileList[i].targetPath);
all += rawPath + delimiter + targetPath + '\n';
}
var listFile;
if (platform == 'win32') {
listFile = path.join(__dirname, 'win/list.txt');
}
else {
listFile = path.join(__dirname, 'mac/list.txt');
}
try {
fs.writeFileSync(listFile, all, 'utf8');//将字符串写入list.txt
}
catch(e) {
return e.message;
}
return zipFiles(listFile);
}

exports.createDbkFile2 = createDbkFile2;

保存中注意的问题

1)过滤掉重复文件

2)保存失败怎么办,我们这里处理可重试三次

3)一个已存在文件再次保存时失败不应该将已存在的正确文件覆盖掉

针对第一个问题,过滤重复文件的代码如下:

static distinctList(inList) {
var outList = [], distinct = {};
var i, len = inList.length;
for (i = 0; i < len; i++) {
var obj = inList[i];
if (!distinct[obj.targetPath]) {
outList.push(obj);
distinct[obj.targetPath] = true;
}
}
return outList;
}

针对上述第三个问题,我们先保存到path+“.temp”文件,成功后再将源文件删除,将path+“.temp”文件重命名为要保存的文件名

public createDbkFile(path, dtoCore, onSuccess: Function) {
var version = "";
var that = this;
this.getDtoFileList(dtoCore, true, true, function (bool, fileList) {//fileList要压缩的文件列表
if (!bool) {
onSuccess(bool);
return;
}
//将dtoCore对象的clipImage置空,减少context的大小
dtoCore.frames.frames.foreach(function (i, obj) {
obj.clipImage = null;
})
//净化dtoCore
dtoCore.fontsUsed = null;
dtoCore.textsUsed = null;
var dtoCoreObj = JSON.decycle(dtoCore, true);
var packageConfig = JSON.stringify(dtoCoreObj);
var dbkTempPath = path + ".temp";//保存文件的临时文件名
Common.FileSytem.createSmallDbk(packageConfig, fileList, dbkTempPath, (e) => {//temp临时文件成功后的回调函数
if (e) {
Common.Logger.setErrLog(Common.LogCode.savedbk, "文件:Editor,方法:createDbkFile,异常信息:" + e);
onSuccess(false);
}
else {
try {
if (Common.FileSytem.existsSync(path)) {//判断是否已经存在该文件
Common.FileSytem.fsExt.removeSync(path);
}
Common.FileSytem.renameSync(dbkTempPath, path.replace(/\\/g, "/").split("/").pop());
if (fileList.get(0) && (fileList.get(0).targetPath == "cover.png") && !editor.isUploadFile) {
index.template.saveLocal(path.replace(/\\/g, "/"), fileList.get(0).rawPath);
}
if (Common.FileSytem.checkZipIntegrity(path)) {//检查zip文件完整性
onSuccess(true);
} else {
Common.Logger.setErrLog(Common.LogCode.savedbk, "文件:Editor,方法:createDbkFile(1),异常信息:" + e);
onSuccess(false);
}
}
catch (e) {
Common.Logger.setErrLog(Common.LogCode.savedbk, "文件:Editor,方法:createDbkFile,异常信息:" + e);
onSuccess(false);
}
}
});
});
}
//检查zip文件夹完整性,判断保存的zip文件是否正确
static checkZipIntegrity(file: string, fileCount: number = undefined): boolean {
var fd;
var buf = new Buffer(22);
var fs = Common.FileSytem.fsExt;
try {
fd = Common.FileSytem.fsExt.openSync(file, "r");
var stat = fs.fstatSync(fd);
var len = stat.size;
if (len < 22)
throw new Error("file size too small");
var n = Common.FileSytem.fsExt.readSync(fd, buf, 0, 22, len-22);
if (n != 22)
throw new Error("read size error");
//0x50 0x4b 0x05 0x06
if (buf[0] != 0x50 || buf[1] != 0x4b || buf[2] != 0x05 || buf[3] != 0x06)
throw new Error("zip Integrity head Error");
var fc = buf[8] | (buf[9] << 8);
if (fileCount && (fc != fileCount))
throw new Error("zip Integrity fileCount Error");
Common.FileSytem.fsExt.closeSync(fd);
return true;
}
catch (e) {
Common.FileSytem.fsExt.closeSync(fd);
return false;
}
}

 

打开文件

跟保存过程可以颠倒着来看整个过程,打开的文件是个dbk文件其实就是zip文件

1. 解压文件

我们需要进行解压缩文件 require(unzip)。  https://github.com/EvanOxfeld/node-unzip

//打开dbk
static openDbk(filePath, isSetFilePath, callback) {
//解压
var unzip = require('unzip');
var that = this;
try
{
var extractTempFolder = FileSytem.tempDbkPath + Util.getGenerate();//创建一个解压目录
FileSytem.mkdirpSync(extractTempFolder);

var readStream = this.fs.createReadStream(filePath);//读取zip文件包
var unzipExtractor = unzip.Extract({ path: extractTempFolder })
unzipExtractor.on('error', function (err) {
callback(false);
});
unzipExtractor.on('close',() => {
that.copyExtratedDbkFile(extractTempFolder + "/dbk",(isSuccess) => {//解压完成后,在进行copy过程,将解压出来的资源copy对应的目录下
isSuccess && isSetFilePath && index.template.saveLocal(filePath.replace(/\\/g, "/"), extractTempFolder + "/dbk/cover.png");
callback(isSuccess, extractTempFolder + "/dbk");
})
});
readStream.pipe(unzipExtractor);

}
catch (e) {
callback(false);
}
}

2. 批量copy过程

解压完成后,再进行copy过程,将解压出来的资源copy对应的目录下。

基于我们的作品包含的文件可能会很多,我们通过模块(lazystream)来实现边读编写的实现复制功能。

https://github.com/jpommerening/node-lazystream 

 

private static copyExtratedDbkFile(sourcePath, callBack) {
var lazyStream = require('lazystream');
//获取所有文件列表遍历读写到对应目录
this.DirUtil.Files(sourcePath, 'file',(err, result: Array<String>) => {
if (result && result.length > 0) {
var steps = 0;
function step() {
steps++;
if (steps >= result.length) {
callBack(true);
}
}

result.forEach((value: string, index: number) => {
var fileName = value;
if (fileName.toLowerCase().indexOf(".ttf") > -1 || fileName.toLowerCase().indexOf(".otf") > -1) {
step();
}
else if (fileName.toLowerCase().replace(/\\/g, "/").indexOf("/frame/") > -1) {//copy文件为缩略图的话,变更目标地址
var frameName = FileSytem.path.basename(fileName);
var readable = new lazyStream.Readable(function () {
return FileSytem.fs.createReadStream(fileName)
});
var writable = new lazyStream.Writable(() => {
return FileSytem.fs.createWriteStream(editor.canvas.canvasImp.framePath + frameName).on('close', function () {
step();
});
});
}
else {
var dest = fileName.replace(/\\/g, "/").replace(sourcePath + "/", 'slideview/');
var readable = new lazyStream.Readable(function () {
return FileSytem.fs.createReadStream(fileName)
});

var writable = new lazyStream.Writable(() => {
return FileSytem.fs.createWriteStream(dest).on('close', function () {
step();
});
});
}
if (readable) {//读文件流 写文件流
readable.pipe(writable);
}
});
}
}, null);
}

3. 获取一个目录下的所有文件

复制的过程,我们是一个一个文件进行读写,在复制之前我们用dirUtil.File获取到了目录下所有文件。

 

//获取一个目录下的所有子目录,所有文件的方法

export class Dir {
fs = require('fs');
path = require('path');

/**
* find all files or subdirs (recursive) and pass to callback fn
*
* @param {string} dir directory in which to recurse files or subdirs
* @param {string} type type of dir entry to recurse ('file', 'dir', or 'all', defaults to 'file')
* @param {function(error, <Array.<string>)} callback fn to call when done
* @example
* dir.files(__dirname, function(err, files) {
*      if (err) throw err;
*      console.log('files:', files);
*  });
*/
Files(dir, type, callback, /* used internally */ ignoreType) {
var that = this;
var pending,
results = {
files: [],
dirs: []
};
var done = function () {
if (ignoreType || type === 'all') {
callback(null, results);
} else {
callback(null, results[type + 's']);
}
};

var getStatHandler = function (statPath) {
return function (err, stat) {
if (err) return callback(err);
if (stat && stat.isDirectory() && stat.mode !== 17115) {
if (type !== 'file') {
results.dirs.push(statPath);
}
that.Files(statPath, type, function (err, res) {
if (err) return callback(err);
if (type === 'all') {
results.files = results.files.concat(res.files);
results.dirs = results.dirs.concat(res.dirs);
} else if (type === 'file') {
results.files = results.files.concat(res.files);
} else {
results.dirs = results.dirs.concat(res.dirs);
}
if (!--pending) done();
}, true);
} else {
if (type !== 'dir') {
results.files.push(statPath);
}
// should be the last statement in statHandler
if (!--pending) done();
}
};
};

if (typeof type !== 'string') {
ignoreType = callback;
callback = type;
type = 'file';
}

this.fs.stat(dir, function (err, stat) {
if (err) return callback(err);
if (stat && stat.mode === 17115) return done();

that.fs.readdir(dir, function (err, list) {
if (err) return callback(err);
pending = list.length;
if (!pending) return done();
for (var file, i = 0, l = list.length; i < l; i++) {
file = that.path.join(dir, list[i]);
that.fs.stat(file, getStatHandler(file));
}
});
});
}

/**
* find all files and subdirs in  a directory (recursive) and pass them to callback fn
*
* @param {string} dir directory in which to recurse files or subdirs
* @param {boolean} combine whether to combine both subdirs and filepaths into one array (default false)
* @param {function(error, Object.<<Array.<string>, Array.<string>>)} callback fn to call when done
* @example
* dir.paths(__dirname, function (err, paths) {
*     if (err) throw err;
*     console.log('files:', paths.files);
*     console.log('subdirs:', paths.dirs);
* });
* dir.paths(__dirname, true, function (err, paths) {
*      if (err) throw err;
*      console.log('paths:', paths);
* });
*/
Paths(dir, combine, callback) {

var type;

if (typeof combine === 'function') {
callback = combine;
combine = false;
}

this.Files(dir, 'all', function (err, results) {
if (err) return callback(err);
if (combine) {

callback(null, results.files.concat(results.dirs));
} else {
callback(null, results);
}
}, null);
}

/**
* find all subdirs (recursive) of a directory and pass them to callback fn
*
* @param {string} dir directory in which to find subdirs
* @param {string} type type of dir entry to recurse ('file' or 'dir', defaults to 'file')
* @param {function(error, <Array.<string>)} callback fn to call when done
* @example
* dir.subdirs(__dirname, function (err, paths) {
*      if (err) throw err;
*      console.log('files:', paths.files);
*      console.log('subdirs:', paths.dirs);
* });
*/
Subdirs(dir, callback) {
this.Files(dir, 'dir', function (err, subdirs) {
if (err) return callback(err);
callback(null, subdirs);
}, null);
}
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: