UNPKG

5.58 kBPlain TextView Raw
1import fs from 'fs';
2import lockFile from 'lockfile';
3import logger from '../core/logger';
4
5export default class LockFileInstance {
6 private filePath: string;
7 private lockKey: string;
8 private logger: any;
9 private tryMax: number;
10 private tryGap: number;
11 private tryCount: number;
12
13 constructor(filePath: string, lockKey: string) {
14 this.filePath = filePath;
15 this.lockKey = lockKey;
16 this.logger = logger({
17 debug: false,
18 silent: true
19 });
20 this.tryMax = 50;
21 this.tryGap = 100;
22 this.tryCount = 0;
23 }
24
25 clearFile(): void {
26 fs.writeFileSync(this.filePath, JSON.stringify({}, null, 2), 'utf-8');
27 }
28
29 checkIfCanRead(cb: any): void {
30 lockFile.check(this.lockKey, (err, status) => {
31 if (err) return this.logger.error(err);
32 if (status) {
33 // another writing is runing
34 if (this.tryCount >= this.tryMax) {
35 this.tryCount = 0;
36 return this.logger.error(`file read time out ${this.filePath}`);
37 }
38 this.tryCount++;
39 setTimeout(() => {
40 this.checkIfCanRead(cb);
41 }, this.tryGap);
42 } else {
43 // read immediatelly
44 this.tryCount = 0;
45 cb && cb();
46 }
47 });
48 }
49
50 read(key: string | undefined): Promise<any> {
51 return new Promise(resolve => {
52 try {
53 fs.stat(
54 this.filePath,
55 (err: Error | null, stats: { isFile: () => boolean }) => {
56 if (err) {
57 this.logger.error(err);
58 resolve(undefined);
59 return;
60 }
61 if (!stats) {
62 this.logger.error('no stats');
63 resolve(undefined);
64 return;
65 }
66 if (stats && !stats.isFile()) {
67 resolve(undefined);
68 return;
69 }
70
71 this.checkIfCanRead(() => {
72 fs.readFile(
73 this.filePath,
74 'utf8',
75 (err: Error | null, data: string) => {
76 if (err) {
77 this.logger.error(err);
78 resolve(undefined);
79 return;
80 }
81
82 if (!data) {
83 // no data then turn it to {}
84 this.clearFile();
85 data = '{}';
86 }
87
88 this.logger.debug(
89 `get file: ${this.filePath} => data: ${data}`
90 );
91 try {
92 const jsonObj: object = JSON.parse(data);
93 if (key && !jsonObj[key]) {
94 this.logger.debug(
95 `get key ${key} form data ${this.filePath} => no value find`
96 );
97 resolve(undefined);
98 return;
99 }
100 resolve(key ? jsonObj[key] : jsonObj);
101 } catch (e) {
102 // 写入的文件数据有问题,清空文件,下次重新写入。
103 this.clearFile();
104 this.logger.error(e);
105 resolve(undefined);
106 }
107 }
108 );
109 });
110 }
111 );
112 } catch (err) {
113 this.logger.error(err);
114 resolve(undefined);
115 }
116 });
117 }
118
119 lock(cb: any): void {
120 lockFile.lock(
121 this.lockKey,
122 {
123 wait: this.tryGap,
124 retries: this.tryMax
125 },
126 err => {
127 if (err) {
128 this.logger.error(err);
129 this.unlock();
130 cb && cb(err);
131 return;
132 }
133
134 cb && cb();
135 }
136 );
137 }
138
139 unlock(): void {
140 lockFile.unlock(this.lockKey, er => {
141 er && this.logger.error('er', er);
142 });
143 }
144
145 update(key: string, value: any): Promise<object | undefined> {
146 return new Promise(resolve => {
147 try {
148 if (!fs.existsSync(this.filePath)) {
149 // 文件不存在则创建文件后插入
150 this.clearFile();
151 }
152 if (!key || Object.prototype.toString.call(key) !== '[object String]') {
153 this.logger.error(`write file ${this.filePath} key not valid ${key}`);
154 resolve(undefined);
155 return;
156 }
157 this.lock((err: Error | undefined | null) => {
158 if (err) {
159 this.unlock();
160 resolve(undefined);
161 return;
162 }
163
164 let data = fs.readFileSync(this.filePath, 'utf-8');
165 if (!data) {
166 // no data then turn it to {}
167 this.clearFile();
168 data = '{}';
169 }
170 try {
171 const jsonObj: object = JSON.parse(data);
172 this.logger.debug(
173 `write file ${this.filePath} key ${key} value ${value}`
174 );
175 jsonObj[key] = value;
176 fs.writeFile(
177 this.filePath,
178 JSON.stringify(jsonObj, null, 2),
179 (err: Error | null) => {
180 this.unlock();
181 if (err) {
182 this.logger.error(err);
183 resolve(undefined);
184 return;
185 }
186 resolve(jsonObj);
187 }
188 );
189 } catch (e) {
190 // 文件数据有问题,清空文件,下次重新写入。
191 this.clearFile();
192 this.logger.error(e);
193 this.unlock();
194 resolve(undefined);
195 }
196 });
197 } catch (err) {
198 this.logger.error(err);
199 this.unlock();
200 resolve(undefined);
201 }
202 });
203 }
204}