react-native-update 10.37.5 → 10.37.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * Use these variables when you tailor your ArkTS code. They must be of the const type.
3
3
  */
4
- export const HAR_VERSION = '3.1.0-0.0.7';
4
+ import packageJson from '../../package.json'
5
+
6
+ export const HAR_VERSION = packageJson.version;
5
7
  export const BUILD_MODE_NAME = 'debug';
6
8
  export const DEBUG = true;
7
9
  export const TARGET_NAME = 'default';
@@ -5,6 +5,7 @@ import { zlib } from '@kit.BasicServicesKit';
5
5
  import { EventHub } from './EventHub';
6
6
  import { DownloadTaskParams } from './DownloadTaskParams';
7
7
  import Pushy from 'librnupdate.so';
8
+ import { saveFileToSandbox } from './SaveFile';
8
9
 
9
10
  interface ZipEntry {
10
11
  filename: string;
@@ -18,7 +19,6 @@ interface ZipFile {
18
19
  export class DownloadTask {
19
20
  private context: common.Context;
20
21
  private hash: string;
21
- private readonly DOWNLOAD_CHUNK_SIZE = 4096;
22
22
  private eventHub: EventHub;
23
23
 
24
24
  constructor(context: common.Context) {
@@ -53,27 +53,104 @@ export class DownloadTask {
53
53
  private async downloadFile(params: DownloadTaskParams): Promise<void> {
54
54
  const httpRequest = http.createHttp();
55
55
  this.hash = params.hash;
56
+ let writer: fileIo.File | null = null;
57
+ let contentLength = 0;
58
+ let received = 0;
59
+ let writeError: Error | null = null;
60
+ let writeQueue = Promise.resolve();
61
+
62
+ const closeWriter = async () => {
63
+ if (writer) {
64
+ await fileIo.close(writer);
65
+ writer = null;
66
+ }
67
+ };
68
+
69
+ const dataEndPromise = new Promise<void>((resolve, reject) => {
70
+ httpRequest.on('dataEnd', () => {
71
+ writeQueue
72
+ .then(async () => {
73
+ if (writeError) {
74
+ throw writeError;
75
+ }
76
+ await closeWriter();
77
+ resolve();
78
+ })
79
+ .catch(async error => {
80
+ await closeWriter();
81
+ reject(error);
82
+ });
83
+ });
84
+ });
56
85
 
57
86
  try {
58
- try {
59
- let exists = fileIo.accessSync(params.targetFile);
60
- if (exists) {
61
- await fileIo.unlink(params.targetFile);
62
- } else {
63
- const targetDir = params.targetFile.substring(
64
- 0,
65
- params.targetFile.lastIndexOf('/'),
66
- );
67
- exists = fileIo.accessSync(targetDir);
68
- if (!exists) {
69
- await fileIo.mkdir(targetDir);
70
- }
87
+ let exists = fileIo.accessSync(params.targetFile);
88
+ if (exists) {
89
+ await fileIo.unlink(params.targetFile);
90
+ } else {
91
+ const targetDir = params.targetFile.substring(
92
+ 0,
93
+ params.targetFile.lastIndexOf('/'),
94
+ );
95
+ exists = fileIo.accessSync(targetDir);
96
+ if (!exists) {
97
+ await fileIo.mkdir(targetDir);
71
98
  }
72
- } catch (error) {
73
- throw error;
74
99
  }
75
100
 
76
- const response = await httpRequest.request(params.url, {
101
+ writer = await fileIo.open(
102
+ params.targetFile,
103
+ fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE,
104
+ );
105
+
106
+ httpRequest.on('headersReceive', (header: Record<string, string>) => {
107
+ if (!header) {
108
+ return;
109
+ }
110
+ const lengthKey = Object.keys(header).find(
111
+ key => key.toLowerCase() === 'content-length',
112
+ );
113
+ if (!lengthKey) {
114
+ return;
115
+ }
116
+ const length = parseInt(header[lengthKey], 10);
117
+ if (!Number.isNaN(length)) {
118
+ contentLength = length;
119
+ }
120
+ });
121
+
122
+ httpRequest.on('dataReceive', (data: ArrayBuffer) => {
123
+ if (writeError) {
124
+ return;
125
+ }
126
+ received += data.byteLength;
127
+ writeQueue = writeQueue.then(async () => {
128
+ if (!writer || writeError) {
129
+ return;
130
+ }
131
+ try {
132
+ await fileIo.write(writer.fd, data);
133
+ } catch (error) {
134
+ writeError = error as Error;
135
+ }
136
+ });
137
+ this.onProgressUpdate(received, contentLength);
138
+ });
139
+
140
+ httpRequest.on(
141
+ 'dataReceiveProgress',
142
+ (data: http.DataReceiveProgressInfo) => {
143
+ if (data.totalSize > 0) {
144
+ contentLength = data.totalSize;
145
+ }
146
+ if (data.receiveSize > received) {
147
+ received = data.receiveSize;
148
+ }
149
+ this.onProgressUpdate(received, contentLength);
150
+ },
151
+ );
152
+
153
+ const responseCode = await httpRequest.requestInStream(params.url, {
77
154
  method: http.RequestMethod.GET,
78
155
  readTimeout: 60000,
79
156
  connectTimeout: 60000,
@@ -81,32 +158,14 @@ export class DownloadTask {
81
158
  'Content-Type': 'application/octet-stream',
82
159
  },
83
160
  });
84
- if (response.responseCode > 299) {
85
- throw Error(`Server error: ${response.responseCode}`);
161
+ if (responseCode > 299) {
162
+ throw Error(`Server error: ${responseCode}`);
86
163
  }
87
164
 
88
- const contentLength = parseInt(response.header['content-length'] || '0');
89
- const writer = await fileIo.open(
90
- params.targetFile,
91
- fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE,
92
- );
93
- let received = 0;
94
- const data = response.result as ArrayBuffer;
95
- const chunks = Math.ceil(data.byteLength / this.DOWNLOAD_CHUNK_SIZE);
96
- for (let i = 0; i < chunks; i++) {
97
- const start = i * this.DOWNLOAD_CHUNK_SIZE;
98
- const end = Math.min(start + this.DOWNLOAD_CHUNK_SIZE, data.byteLength);
99
- const chunk = data.slice(start, end);
100
-
101
- await fileIo.write(writer.fd, chunk);
102
- received += chunk.byteLength;
103
-
104
- this.onProgressUpdate(received, contentLength);
105
- }
106
- await fileIo.close(writer);
165
+ await dataEndPromise;
107
166
  const stats = await fileIo.stat(params.targetFile);
108
167
  const fileSize = stats.size;
109
- if (fileSize !== contentLength) {
168
+ if (contentLength > 0 && fileSize !== contentLength) {
110
169
  throw Error(
111
170
  `Download incomplete: expected ${contentLength} bytes but got ${stats.size} bytes`,
112
171
  );
@@ -115,6 +174,15 @@ export class DownloadTask {
115
174
  console.error('Download failed:', error);
116
175
  throw error;
117
176
  } finally {
177
+ try {
178
+ await closeWriter();
179
+ } catch (closeError) {
180
+ console.error('Failed to close file:', closeError);
181
+ }
182
+ httpRequest.off('headersReceive');
183
+ httpRequest.off('dataReceive');
184
+ httpRequest.off('dataReceiveProgress');
185
+ httpRequest.off('dataEnd');
118
186
  httpRequest.destroy();
119
187
  }
120
188
  }
@@ -166,7 +234,8 @@ export class DownloadTask {
166
234
 
167
235
  return { entries };
168
236
  } catch (error) {
169
- console.error('Failed to process unzipped files:', error);
237
+ error.message = 'Failed to process unzipped files:' + error.message;
238
+ console.error(error);
170
239
  throw error;
171
240
  }
172
241
  }
@@ -195,10 +264,6 @@ export class DownloadTask {
195
264
 
196
265
  const copies = obj.copies as Record<string, string>;
197
266
  for (const [to, rawPath] of Object.entries(copies)) {
198
- if (!rawPath.startsWith('resources/rawfile/')) {
199
- // skip other resource
200
- continue;
201
- }
202
267
  let from = rawPath.replace('resources/rawfile/', '');
203
268
  if (from === '') {
204
269
  from = to;
@@ -244,7 +309,7 @@ export class DownloadTask {
244
309
  await fileIo.close(writer);
245
310
  continue;
246
311
  } catch (error) {
247
- console.error('Failed to process bundle patch:', error);
312
+ error.message = 'Failed to process bundle patch:' + error.message;
248
313
  throw error;
249
314
  }
250
315
  }
@@ -365,15 +430,27 @@ export class DownloadTask {
365
430
 
366
431
  for (const [from, targets] of copyList.entries()) {
367
432
  currentFrom = from;
368
- const fromContent = await resourceManager.getRawFileContent(from);
433
+ if (from.startsWith('resources/base/media/')) {
434
+ const mediaName = from
435
+ .replace('resources/base/media/', '')
436
+ .split('.')[0];
437
+ const mediaBuffer = await resourceManager.getMediaByName(mediaName);
438
+ for (const target of targets) {
439
+ const fileStream = fileIo.createStreamSync(target, 'w+');
440
+ fileStream.writeSync(mediaBuffer);
441
+ fileStream.close();
442
+ }
443
+ continue;
444
+ }
445
+ const fromContent = await resourceManager.getRawFd(from);
369
446
  for (const target of targets) {
370
- const fileStream = fileIo.createStreamSync(target, 'w+');
371
- fileStream.writeSync(fromContent.buffer);
372
- fileStream.close();
447
+ saveFileToSandbox(fromContent, target);
373
448
  }
374
449
  }
375
450
  } catch (error) {
376
- console.error('Copy from resource failed:', currentFrom, error.message);
451
+ error.message =
452
+ 'Copy from resource failed:' + currentFrom + ',' + error.message;
453
+ console.error(error);
377
454
  throw error;
378
455
  }
379
456
  }
@@ -406,7 +483,8 @@ export class DownloadTask {
406
483
  }
407
484
  }
408
485
  } catch (error) {
409
- console.error('Cleanup failed:', error);
486
+ error.message = 'Cleanup failed:' + error.message;
487
+ console.error(error);
410
488
  throw error;
411
489
  }
412
490
  }
@@ -432,7 +510,6 @@ export class DownloadTask {
432
510
  default:
433
511
  throw Error(`Unknown task type: ${params.type}`);
434
512
  }
435
-
436
513
  } catch (error) {
437
514
  console.error('Task execution failed:', error.message);
438
515
  if (params.type !== DownloadTaskParams.TASK_TYPE_CLEANUP) {
@@ -1,8 +1,7 @@
1
1
  import {
2
2
  FileJSBundle,
3
- HotReloadConfig,
4
3
  JSBundleProvider,
5
- JSBundleProviderError
4
+ JSBundleProviderError,
6
5
  } from '@rnoh/react-native-openharmony';
7
6
  import common from '@ohos.app.ability.common';
8
7
  import fs from '@ohos.file.fs';
@@ -10,7 +9,7 @@ import { UpdateContext } from './UpdateContext';
10
9
 
11
10
  export class PushyFileJSBundleProvider extends JSBundleProvider {
12
11
  private updateContext: UpdateContext;
13
- private path: string = ''
12
+ private path: string = '';
14
13
 
15
14
  constructor(context: common.UIAbilityContext) {
16
15
  super();
@@ -26,24 +25,26 @@ export class PushyFileJSBundleProvider extends JSBundleProvider {
26
25
  if (!this.path) {
27
26
  throw new JSBundleProviderError({
28
27
  whatHappened: 'No pushy bundle found. using default bundle',
29
- howCanItBeFixed: ['']
30
- })
28
+ howCanItBeFixed: [''],
29
+ });
31
30
  }
32
31
  try {
33
32
  await fs.access(this.path, fs.OpenMode.READ_ONLY);
34
33
  return {
35
- filePath: this.path
36
- }
34
+ filePath: this.path,
35
+ };
37
36
  } catch (error) {
38
37
  throw new JSBundleProviderError({
39
38
  whatHappened: `Couldn't load JSBundle from ${this.path}`,
40
39
  extraData: error,
41
- howCanItBeFixed: [`Check if a bundle exists at "${this.path}" on your device.`]
42
- })
40
+ howCanItBeFixed: [
41
+ `Check if a bundle exists at "${this.path}" on your device.`,
42
+ ],
43
+ });
43
44
  }
44
45
  }
45
46
 
46
47
  getAppKeys(): string[] {
47
48
  return [];
48
49
  }
49
- }
50
+ }
@@ -0,0 +1,34 @@
1
+ import resourceManager from '@ohos.resourceManager';
2
+ import fs, { ReadOptions } from '@ohos.file.fs';
3
+
4
+ export const saveFileToSandbox = (
5
+ from: resourceManager.RawFileDescriptor,
6
+ toPath: string,
7
+ ) => {
8
+ let to = fs.openSync(toPath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
9
+
10
+ let bufferSize = 30000;
11
+ let buffer = new ArrayBuffer(bufferSize); // 创建buffer缓冲区
12
+ // 要copy的文件的offset和length
13
+ let currentOffset = from.offset;
14
+ let readOption: ReadOptions = {
15
+ offset: currentOffset, // 期望读取文件的位置。可选,默认从当前位置开始读
16
+ length: bufferSize, // 每次期望读取数据的长度。可选,默认缓冲区长度
17
+ };
18
+ // 后面len会一直减,直到没有
19
+ while (true) {
20
+ // 读取buffer容量的内容
21
+ let readLength = fs.readSync(from.fd, buffer, readOption);
22
+ // 写入buffer容量的内容
23
+ fs.writeSync(to.fd, buffer, { length: readLength });
24
+ // 判断后续内容,修改读文件的参数
25
+ // buffer没读满代表文件读完了
26
+ if (readLength < bufferSize) {
27
+ break;
28
+ }
29
+ if (readOption.offset) {
30
+ readOption.offset += readLength;
31
+ }
32
+ }
33
+ fs.close(to);
34
+ };
@@ -69,7 +69,7 @@ export class UpdateModuleImpl {
69
69
  ): Promise<boolean> {
70
70
  const hash = options.hash;
71
71
  if (!hash) {
72
- throw Error('hash不能为空');
72
+ throw Error('empty hash');
73
73
  }
74
74
 
75
75
  try {
@@ -87,7 +87,7 @@ export class UpdateModuleImpl {
87
87
  return true;
88
88
  } catch (error) {
89
89
  logger.error(TAG, `markSuccess failed: ${error}`);
90
- throw Error(`执行报错: ${error.message}`);
90
+ throw error;
91
91
  }
92
92
  }
93
93
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-update",
3
- "version": "10.37.5",
3
+ "version": "10.37.7",
4
4
  "description": "react-native hot update",
5
5
  "main": "src/index",
6
6
  "scripts": {