使用phonegap / cordova文件插件第2部分的问题 - 同步性

时间:2014-09-04 15:03:39

标签: file cordova phonegap-plugins cordova-3

我想在我的cordova应用程序中添加一些简单的日志记录功能。

所以我添加了文件插件,实现了一个超级简单的日志方法并进行了测试。

我的配置:

    $ cordova --version
    3.5.0-0.2.7

    $ cordova plugins
    org.apache.cordova.file 1.3.0 "File"

测试设备是华为u8850,运行Android 2.3.5

记录器:

window.MyLog = {
    log: function(line){
        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(FS) {
            FS.root.getFile('the_log3.txt', {"create":true, "exclusive":false},
                function(fileEntry) {
                    fileEntry.createWriter(
                        function(writer) {
                            console.log(line);
                            writer.seek(writer.length);     // append to eof
                            writer.write(line + '\n');      // write the line
                        }, fail);
                }, fail);
        }, fail);
    }
};

测试:

    MyLog.log('A ' + new Date().toLocaleTimeString());
    MyLog.log('B ' + new Date().toLocaleTimeString());
    MyLog.log('C ' + new Date().toLocaleTimeString());

我启动了3次应用,预计......像

A 16:03:47
B 16:03:47
C 16:03:47
A 16:04:34
B 16:04:34
C 16:04:34
A 16:04:41
B 16:04:41
C 16:04:41

但得到了这个

C 16:03:47
C 16:04:34
A 16:04:41

有两个问题:

  1. 并非所有行都在日志中
  2. 未定义哪一行恰好被写入
  3. 我认为问题的原因是文件方法的异步性质。但这只是猜测。

    问题是:

    1. 如何实现将行附加到文本文件的方法,确保没有行丢失,行的顺序与对方法的调用相同?
    2. 我是否必须使用文件方法的同步版本?
    3. 如果是:是否有可用的文档/示例代码?

2 个答案:

答案 0 :(得分:1)

可能你在之前的写作完成之前尝试写,并解雇了onwriteend事件。

正如我在第一个问题的回答中所述,实现caching功能是个好主意。

因此,您的所有日志都将存储在临时cache中。每次向此cache添加内容时,您都会检查它的大小,一旦达到您定义的限制,请调用logDumping方法,该方法将是实际的write日志文件。

当您调用dump方法时,您可以将日志内容传递给编写者并清空缓存,这样您就可以在其中存储新内容,并且它不会与已经记录重叠内容。

我要做的是:

  1. cache日志
  2. 检查cache是否达到了尺寸限制
  3. cache的内容传递给tmp_var并清除cache
  4. tmp_var写入logfile
  5. 检查onwriteend>如果成功清除tmp_var,如果出现错误,您可以将tmp_var写回实际的cache(因此不会丢失任何数据),并尝试编写{{1}的内容再次登录你的日志文件。

答案 1 :(得分:1)

#include "stdio.h"

void appendLine(char* path_to_file, char* line) {
    FILE* fp = fopen(path_to_file, "a");
    if (fp) {
        fprintf(fp, "%s\n", line);
        fclose(fp);
    }
}

int main(){
    appendLine("logfile.txt", "One Line");
    appendLine("logfile.txt", "Another Line");
    return 0;
}

这是用一种叫做“C”的古老语言写成的。

只需4行代码:

  1. fopen:打开一个现有文件或创建一个新文件,文件位置在末尾(“a”表示追加)
  2. if(fp):检查是否创建了文件对象
  3. fprintf:做吧!
  4. fclose:关闭它(并刷新内容)
  5. 可以使用以下命令编译:

    gcc -Wall addline.c -o addline
    

    并且这样开始:

    ./addline
    

    输出将是这样的:

    One Line
    Another Line
    

    不幸的是,在使用phonegap / cordova编写基于JS的应用程序时无法做到这一点。

    由于未知原因,基本文件操作的简单同步接口不是phonegaps核心实现的一部分,另外不包含在'file'插件中。

    所以我花了一些时间来实现一个简单的TextFile包装器来完成脏工作,所以客户端应用程序可以使用这样的简单命令:

    // a single object is the wrapper:
    var textFile;
    
    function init() {
        // ...
    
        // initialize the (log) file
        // the file will be created if doesn't exists
        // when sth. goes wrong, the callback has an error message
        textFile = new TextFile('logfile.txt', function(ok, msg){
            if (!ok) {
                alert('logging not available' + (msg ? '\nError: ' + msg : ''));
                textFile = null;
            } else {
                textFile.writeLine('start logging ...');
                start();
            }
        },
        {
            // optional options, currently just one property 'clear' is supported
            // if set to true, the file will be emptied at start
            clear: true
        }
    );
    
    
    // later, use methods
    
    // append some lines
    textFile.writeLine('a line');
    textFile.writeLine('another line');
    
    // get content, callback needed since it is not synchronous
    textFile.getContent(function(text){
        // show text
    });
    
    // empty file
    textFile.clear();
    
    // remove file
    textFile.remove();
    

    我使用jquery来合并选项 - 如果jquery不合适,只需替换$ .extend方法。

    这是代码:

    /**
     * Created by martin on 9/3/14.
     *
     * requires the file plugin:
     *
     * cordova plugin add org.apache.cordova.file
     *
     * implemented and tested with
     *   cordova-3.5.0-0.2.7
     * on
     *   Android 2.3.5
     */
    
    
    
    (function(){
        'use strict';
    
        // these values are NOT part of FileWriter, so we had to define them:
        const INIT = 0;
        const WRITING = 1;
        const DONE = 2;
    
        function errMessage(code){
            var msg = '';
    
            switch (code) {
                case FileError.QUOTA_EXCEEDED_ERR:
                    msg = 'QUOTA_EXCEEDED_ERR';
                    break;
                case FileError.NOT_FOUND_ERR:
                    msg = 'NOT_FOUND_ERR';
                    break;
                case FileError.SECURITY_ERR:
                    msg = 'SECURITY_ERR';
                    break;
                case FileError.INVALID_MODIFICATION_ERR:
                    msg = 'INVALID_MODIFICATION_ERR';
                    break;
                case FileError.INVALID_STATE_ERR:
                    msg = 'INVALID_STATE_ERR';
                    break;
                default:
                    msg = 'Unknown Error';
                    break;
            };
    
            return msg;
        }
    
        /**
         *
         * @param fileName  : the name of the file for which the TextFile is created
         * @param cb        : function, is called when
         *                    - TextFile is created, parameter: boolean true
         *                    - TextFile is not created, parameters: boolean false, string error_message
         * @param options   : object with single property
         *                    - boolean clear - truncates the file, defaults to false
         * @constructor
         */
        window.TextFile = function(fileName, cb, options){
            this.writer = null;
            this.queue = [];
    
            this.fileEntry = null;
    
            this._initialize(fileName, cb, options);
        }
    
        var files = {};
    
        TextFile.prototype = {
            // pseudo private method, called form constructor
            _initialize: function(fileName, cb, options){
    
                this.options = $.extend({startMsg: null, clear: false}, options)
    
                if (files.fileName) {
                    cb(false, 'TextFile[' + fileName + '] already in use');
                }
                files.fileName = true;
                var that = this;
                window.requestFileSystem(
                    LocalFileSystem.PERSISTENT,
                    0,
                    function(FS) {
                        FS.root.getFile(
                            fileName,
                            {
                                'create': true,
                                'exclusive': false
                            },
                            function(fileEntry) {
                                that.fileEntry = fileEntry;
                                fileEntry.createWriter(
                                    function(writer) {
                                        that.writer = writer;
                                        writer.seek(writer.length);
                                        writer.onwriteend = function(){
                                            if (that.queue.length > 0){
                                                // sth in the queue
                                                var item = that.queue[0];
                                                switch (item.type) {
                                                    case 'w':
                                                        writer.write(item.line + '\n');
                                                        break;
                                                    case 't':
                                                        writer.truncate(0);
                                                        break;
                                                    case 'g':
                                                        that._readContent(item.cb);
                                                        break;
    
                                                    default:
                                                        throw 'unknown type ' + item.type;
                                                }
    
                                                // get rid of processed item
                                                that.queue.splice(0, 1);
                                            }
                                        }
                                        if (that.options.clear) {
                                            that.clear();
                                        }
                                        cb(true);
                                    }
                                );
                            },
                            function(err){
                                cb(false, errMessage(err.code))
                            }
                        );
                    },
                    function(){
                        cb(false, errMessage(err.code));
                    }
                );
            },
    
            // internal use
            _readContent: function(cb){
                this.fileEntry.file(function(file) {
                        var reader = new FileReader();
    
                        reader.onloadend = function(e) {
                            var res = this.result;
                            cb(res);
                        };
    
                        reader.readAsText(file);
                    },
                    function(err) {
                        cb('got an error: ' + errMessage(err.code));
                    }
                );
            },
    
            // reads whole file, content is sent to client with callback
            getContent: function(cb){
                if (this.writer.readyState !== WRITING) {
                    this._readContent(cb);
                } else {
                    this.queue.push({type: 'g', cb:cb});
                }
            },
    
            // set file size to zero
            clear: function() {
                if (this.writer.readyState !== WRITING) {
                    this.writer.truncate(0);
                } else {
                    this.queue.push({type: 't'});
                }
            },
    
            // removes file
            remove: function(cb){
                this.fileEntry.remove(
                    function(){
                        if (cb) {
                            cb(true);
                        }
                    },
                    function(err){
                        if (cb) {
                            cb(false,errMessage(err.code));
                        }
                    }
                );
            },
    
            // append single line to file
            writeLine: function(line){
                if (this.writer.readyState !== WRITING) {
                    this.writer.write(line + '\n');
                } else {
                    this.queue.push({type: 'w', line: line});
                }
            }
        };
    })();
    

    也许这对其他人有用,在同样的问题上挣扎......