react-native-blob-util 0.21.3 → 0.22.1

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,4 +1,5 @@
1
1
  #include "pch.h"
2
+
2
3
  #include "ReactNativeBlobUtil.h"
3
4
  #include <winrt/Windows.ApplicationModel.Activation.h>
4
5
  #include <winrt/Windows.Security.Cryptography.h>
@@ -9,6 +10,7 @@
9
10
  #include <winrt/Windows.Web.Http.h>
10
11
  #include <winrt/Windows.Web.Http.Headers.h>
11
12
  #include <winrt/windows.web.http.filters.h>
13
+ #include <winrt/Windows.System.Threading.h>
12
14
  #include <filesystem>
13
15
  #include <sstream>
14
16
 
@@ -108,1406 +110,1554 @@ void TaskCancellationManager::Cancel(TaskId taskId) noexcept
108
110
  }
109
111
  }
110
112
 
111
- ReactNativeBlobUtilConfig::ReactNativeBlobUtilConfig(winrt::Microsoft::ReactNative::JSValueObject& options) {
112
- if (options["appendExt"].IsNull() == true)
113
- {
114
- appendExt = "";
115
- }
116
- else
117
- {
118
- appendExt = options["appendExt"].AsString();
119
- }
120
- fileCache = options["fileCache"].AsBoolean();
121
- followRedirect = options["followRedirect"].AsBoolean();
122
- overwrite = options["overwrite"].AsBoolean();
123
- if (options["path"].IsNull() == true)
124
- {
125
- path = "";
126
- }
127
- else
128
- {
129
- auto filepath{ options["path"].AsString() };
130
- auto fileLength{ filepath.length() };
131
- bool hasTrailingSlash{ filepath[fileLength - 1] == '\\' || filepath[fileLength - 1] == '/' };
132
- std::filesystem::path pathToParse{ hasTrailingSlash ? filepath.substr(0, fileLength - 1) : filepath };
133
- pathToParse.make_preferred();
134
- path = pathToParse.string();
135
- }
136
- trusty = options["trusty"].AsBoolean();
137
-
138
- int64_t potentialTimeout{ options["timeout"].AsInt64() };
139
- timeout = std::chrono::seconds{ potentialTimeout > 0 ? potentialTimeout : 60000 };
140
-
141
- }
142
-
143
- ReactNativeBlobUtilProgressConfig::ReactNativeBlobUtilProgressConfig(int32_t count_, int32_t interval_) : count(count_), interval(interval_) {
144
- }
145
-
146
- void ReactNativeBlobUtil::Initialize(winrt::Microsoft::ReactNative::ReactContext const& reactContext) noexcept
113
+ ReactNativeBlobUtilConfig::ReactNativeBlobUtilConfig(::React::JSValue& options)
147
114
  {
148
- m_reactContext = reactContext;
115
+ auto getStringOrDefault = [](const winrt::Microsoft::ReactNative::JSValue& value, const std::string& defaultValue = "") -> std::string {
116
+ return value.IsNull() ? defaultValue : value.AsString();
117
+ };
118
+
119
+ auto getBoolOrDefault = [](const winrt::Microsoft::ReactNative::JSValue& value, bool defaultValue = false) -> bool {
120
+ return value.IsNull() ? defaultValue : value.AsBoolean();
121
+ };
122
+
123
+ auto getInt64OrDefault = [](const winrt::Microsoft::ReactNative::JSValue& value, int64_t defaultValue = 60000) -> int64_t {
124
+ return value.IsNull() ? defaultValue : value.AsInt64();
125
+ };
126
+
127
+ appendExt = getStringOrDefault(options["appendExt"]);
128
+ fileCache = getBoolOrDefault(options["fileCache"]);
129
+ followRedirect = getBoolOrDefault(options["followRedirect"]);
130
+ overwrite = getBoolOrDefault(options["overwrite"]);
131
+ trusty = getBoolOrDefault(options["trusty"]);
132
+
133
+ // Handle path sanitization
134
+ {
135
+ std::string filepath = getStringOrDefault(options["path"]);
136
+ if (!filepath.empty())
137
+ {
138
+ size_t fileLength = filepath.length();
139
+ bool hasTrailingSlash = filepath[fileLength - 1] == '\\' || filepath[fileLength - 1] == '/';
140
+ std::filesystem::path pathToParse = hasTrailingSlash ? filepath.substr(0, fileLength - 1) : filepath;
141
+ pathToParse.make_preferred();
142
+ path = pathToParse.string();
143
+ }
144
+ else
145
+ {
146
+ path = "";
147
+ }
148
+ }
149
+
150
+ // Timeout handling
151
+ int64_t potentialTimeout = getInt64OrDefault(options["timeout"]);
152
+ timeout = std::chrono::seconds{ potentialTimeout > 0 ? potentialTimeout : 60000 };
153
+ }
154
+
155
+ ReactNativeBlobUtilProgressConfig::ReactNativeBlobUtilProgressConfig(double count_, double interval_) : count(count_), interval(interval_) {
149
156
  }
150
157
 
151
- //
152
- // RNFS implementations
153
- //
154
- void ReactNativeBlobUtil::ConstantsViaConstantsProvider(winrt::Microsoft::ReactNative::ReactConstantProvider& constants) noexcept
155
- {
156
- // ReactNativeBlobUtil.DocumentDir
157
- constants.Add(L"DocumentDir", to_string(ApplicationData::Current().LocalFolder().Path()));
158
-
159
- // ReactNativeBlobUtil.CacheDir
160
- constants.Add(L"CacheDir", to_string(ApplicationData::Current().LocalCacheFolder().Path()));
161
-
162
- // ReactNativeBlobUtil.PictureDir
163
- constants.Add(L"PictureDir", UserDataPaths::GetDefault().Pictures());
164
-
165
- // ReactNativeBlobUtil.MusicDir
166
- constants.Add(L"MusicDir", UserDataPaths::GetDefault().Music());
167
-
168
- // ReactNativeBlobUtil.MovieDir
169
- constants.Add(L"MovieDir", UserDataPaths::GetDefault().Videos());
170
-
171
- // ReactNativeBlobUtil.DownloadDirectoryPath - IMPLEMENT for convenience? (absent in iOS and deprecated in Android)
172
- constants.Add(L"DownloadDir", UserDataPaths::GetDefault().Downloads());
173
-
174
- // ReactNativeBlobUtil.MainBundleDir
175
- constants.Add(L"MainBundleDir", to_string(Package::Current().InstalledLocation().Path()));
176
- }
177
-
178
- // createFile
179
- winrt::fire_and_forget ReactNativeBlobUtil::createFile(
180
- std::string path,
181
- std::wstring content,
182
- std::string encoding,
183
- winrt::Microsoft::ReactNative::ReactPromise<std::string> promise) noexcept
184
- try
185
- {
186
- bool shouldExit{ false };
187
- Streams::IBuffer buffer;
188
- if (encoding.compare("uri") == 0)
189
- {
190
- try
191
- {
192
- winrt::hstring srcDirectoryPath, srcFileName;
193
- splitPath(content, srcDirectoryPath, srcFileName);
194
- StorageFolder srcFolder{ co_await StorageFolder::GetFolderFromPathAsync(srcDirectoryPath) };
195
- StorageFile srcFile{ co_await srcFolder.GetFileAsync(srcFileName) };
196
- buffer = co_await FileIO::ReadBufferAsync(srcFile);
197
- }
198
- catch (...)
199
- {
200
- shouldExit = true;
201
- }
202
- }
203
- else if (encoding.compare("utf8") == 0)
204
- {
205
- buffer = CryptographicBuffer::ConvertStringToBinary(content, BinaryStringEncoding::Utf8);
206
- }
207
- else if (encoding.compare("base64") == 0)
208
- {
209
- buffer = CryptographicBuffer::DecodeFromBase64String(content);
210
- }
211
- else
212
- {
213
- promise.Reject("Invalid encoding");
214
- shouldExit = true;
215
- }
216
-
217
- if (!shouldExit)
218
- {
219
-
220
- winrt::hstring destDirectoryPath, destFileName;
221
- splitPath(path, destDirectoryPath, destFileName);
222
-
223
- auto folder{ co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath) };
224
-
225
- try
226
- {
227
- auto file{ co_await folder.CreateFileAsync(destFileName, CreationCollisionOption::FailIfExists) };
228
- auto stream{ co_await file.OpenAsync(FileAccessMode::ReadWrite) };
229
- co_await stream.WriteAsync(buffer);
230
- }
231
- catch (...)
232
- {
233
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EEXIST", "EEXIST: File already exists; " + path });
234
- shouldExit = true;
235
- }
236
- }
237
- if (!shouldExit)
238
- {
239
- promise.Resolve(path);
240
- }
241
- co_return;
242
- }
243
- catch (const hresult_error& ex)
158
+ ReactNativeBlobUtilStream::ReactNativeBlobUtilStream(Streams::IRandomAccessStream& _streamInstance, EncodingOptions _encoding) noexcept
159
+ : streamInstance{ std::move(_streamInstance) }
160
+ , encoding{ _encoding }
244
161
  {
245
- hresult result{ ex.code() };
246
- if (result == 0x80070002) // FileNotFoundException
247
- {
248
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "ENOENT", "ENOENT: File does not exist and could not be created; " + path });
249
- }
250
- else
251
- {
252
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "EUNSPECIFIED: " + winrt::to_string(ex.message()) + "; " + path });
253
- }
254
162
  }
255
163
 
256
- winrt::fire_and_forget ReactNativeBlobUtil::createFileASCII(
257
- std::string path,
258
- winrt::Microsoft::ReactNative::JSValueArray dataArray,
259
- winrt::Microsoft::ReactNative::ReactPromise<std::string> promise) noexcept
260
- try
164
+ namespace winrt::ReactNativeBlobUtil
261
165
  {
262
- std::vector<uint8_t> data;
263
- data.reserve(dataArray.size());
264
- for (auto& var : dataArray)
265
- {
266
- data.push_back(var.AsUInt8());
267
- }
268
166
 
269
- Streams::IBuffer buffer{ CryptographicBuffer::CreateFromByteArray(data) };
270
-
271
- winrt::hstring directoryPath, fileName;
272
- splitPath(path, directoryPath, fileName);
273
-
274
- StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
275
-
276
- StorageFile file{ co_await folder.CreateFileAsync(fileName, CreationCollisionOption::FailIfExists) };
277
- Streams::IRandomAccessStream stream{ co_await file.OpenAsync(FileAccessMode::ReadWrite) };
278
- co_await stream.WriteAsync(buffer);
279
-
280
- promise.Resolve(path);
281
- }
282
- catch (const hresult_error& ex)
167
+ // See https://microsoft.github.io/react-native-windows/docs/native-modules for details on writing native modules
168
+
169
+ void ReactNativeBlobUtil::Initialize(React::ReactContext const& reactContext) noexcept {
170
+ m_context = reactContext;
171
+ }
172
+
173
+ // Constants method
174
+ ReactNativeBlobUtilCodegen::BlobUtilsSpec_Constants ReactNativeBlobUtil::GetConstants() noexcept {
175
+ ReactNativeBlobUtilCodegen::BlobUtilsSpec_Constants constants;
176
+ constants.DocumentDir = to_string(ApplicationData::Current().LocalFolder().Path());
177
+ constants.CacheDir = to_string(ApplicationData::Current().LocalCacheFolder().Path());
178
+ constants.PictureDir = to_string(UserDataPaths::GetDefault().Pictures());
179
+ constants.MusicDir = to_string(UserDataPaths::GetDefault().Music());
180
+ constants.MovieDir = to_string(UserDataPaths::GetDefault().Videos());
181
+ constants.DownloadDir = to_string(UserDataPaths::GetDefault().Downloads());
182
+ constants.MainBundleDir = to_string(Package::Current().InstalledLocation().Path());
183
+ return constants;
184
+ }
185
+
186
+ winrt::fire_and_forget ReactNativeBlobUtil::fetchBlobForm(
187
+ ::React::JSValue options,
188
+ std::string taskId,
189
+ std::string method,
190
+ std::string url,
191
+ ::React::JSValue headers,
192
+ ::React::JSValueArray body,
193
+ std::function<void(::React::JSValueArray)> callback
194
+ ) noexcept
195
+ {
196
+ try
197
+ {
198
+ winrt::hstring boundary{ L"-----" };
199
+ winrt::Windows::Web::Http::Filters::HttpBaseProtocolFilter filter;
200
+ ReactNativeBlobUtilConfig config{ options };
201
+ filter.AllowAutoRedirect(false);
202
+
203
+ if (config.trusty)
204
+ {
205
+ filter.IgnorableServerCertificateErrors().Append(
206
+ winrt::Windows::Security::Cryptography::Certificates::ChainValidationResult::Untrusted);
207
+ }
208
+
209
+ winrt::Windows::Web::Http::HttpMethod httpMethod = winrt::Windows::Web::Http::HttpMethod::Post();
210
+ if (method == "DELETE" || method == "delete")
211
+ httpMethod = winrt::Windows::Web::Http::HttpMethod::Delete();
212
+ else if (method == "PUT" || method == "put")
213
+ httpMethod = winrt::Windows::Web::Http::HttpMethod::Put();
214
+ else if (method == "GET" || method == "get")
215
+ httpMethod = winrt::Windows::Web::Http::HttpMethod::Get();
216
+ else if (method != "POST" && method != "post")
217
+ {
218
+ ::React::JSValueArray errorArray;
219
+ errorArray.push_back("Method not supported");
220
+ callback(std::move(errorArray));
221
+ co_return;
222
+ }
223
+
224
+ winrt::Windows::Web::Http::HttpRequestMessage requestMessage{ httpMethod, winrt::Windows::Foundation::Uri{ winrt::to_hstring(url) } };
225
+ winrt::Windows::Web::Http::HttpMultipartFormDataContent requestContent{ boundary };
226
+
227
+ // Add headers
228
+ if (headers.ItemCount() > 0)
229
+ {
230
+ for (const auto& entry : headers.AsObject())
231
+ {
232
+ if (!requestMessage.Headers().TryAppendWithoutValidation(
233
+ winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString())))
234
+ {
235
+ requestContent.Headers().TryAppendWithoutValidation(
236
+ winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString()));
237
+ }
238
+ }
239
+ }
240
+
241
+ // Add form data
242
+ for (auto& entry : body)
243
+ {
244
+ auto& items = entry.AsObject();
245
+ auto data = items["data"].AsString();
246
+
247
+ // File upload support: expects "file://" prefix
248
+ bool isFile = data.rfind("file://", 0) == 0;
249
+ if (isFile)
250
+ {
251
+ std::string contentPath = data.substr(strlen("file://"));
252
+ winrt::hstring directoryPath, fileName;
253
+ splitPath(contentPath, directoryPath, fileName);
254
+ auto folder = co_await winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(directoryPath);
255
+ auto storageFile = co_await folder.GetFileAsync(fileName);
256
+ auto requestBuffer = co_await winrt::Windows::Storage::FileIO::ReadBufferAsync(storageFile);
257
+
258
+ winrt::Windows::Web::Http::HttpBufferContent requestBufferContent{ requestBuffer };
259
+ if (!items["type"].IsNull())
260
+ {
261
+ requestBufferContent.Headers().TryAppendWithoutValidation(
262
+ L"content-type", winrt::to_hstring(items["type"].AsString()));
263
+ }
264
+
265
+ auto name = items["name"].IsNull() ? L"" : winrt::to_hstring(items["name"].AsString());
266
+ auto filename = items["filename"].IsNull() ? L"" : winrt::to_hstring(items["filename"].AsString());
267
+ if (name.empty())
268
+ {
269
+ requestContent.Add(requestBufferContent);
270
+ }
271
+ else if (filename.empty())
272
+ {
273
+ requestContent.Add(requestBufferContent, name);
274
+ }
275
+ else
276
+ {
277
+ requestContent.Add(requestBufferContent, name, filename);
278
+ }
279
+ }
280
+ else
281
+ {
282
+ winrt::Windows::Web::Http::HttpStringContent dataContents{ winrt::to_hstring(data) };
283
+ if (!items["type"].IsNull())
284
+ {
285
+ dataContents.Headers().TryAppendWithoutValidation(
286
+ L"content-type", winrt::to_hstring(items["type"].AsString()));
287
+ }
288
+
289
+ auto name = items["name"].IsNull() ? L"" : winrt::to_hstring(items["name"].AsString());
290
+ auto filename = items["filename"].IsNull() ? L"" : winrt::to_hstring(items["filename"].AsString());
291
+ if (name.empty())
292
+ {
293
+ requestContent.Add(dataContents);
294
+ }
295
+ else if (filename.empty())
296
+ {
297
+ requestContent.Add(dataContents, name);
298
+ }
299
+ else
300
+ {
301
+ requestContent.Add(dataContents, name, filename);
302
+ }
303
+ }
304
+ }
305
+
306
+ requestMessage.Content(requestContent);
307
+
308
+ winrt::Windows::Web::Http::HttpClient httpClient{ filter };
309
+ auto response = co_await httpClient.SendRequestAsync(requestMessage);
310
+
311
+ std::string responseBody;
312
+ if (response.Content() != nullptr)
313
+ {
314
+ responseBody = winrt::to_string(co_await response.Content().ReadAsStringAsync());
315
+ }
316
+
317
+ ::React::JSValueArray resultArray;
318
+ resultArray.push_back(responseBody);
319
+ callback(std::move(resultArray));
320
+ }
321
+ catch (const winrt::hresult_error& ex)
322
+ {
323
+ ::React::JSValueArray errorArray;
324
+ errorArray.push_back("EUNSPECIFIED");
325
+ errorArray.push_back(winrt::to_string(ex.message()));
326
+ callback(std::move(errorArray));
327
+ }
328
+ catch (...)
329
+ {
330
+ ::React::JSValueArray errorArray;
331
+ errorArray.push_back("EUNSPECIFIED");
332
+ errorArray.push_back("Unknown error in fetchBlobForm");
333
+ callback(std::move(errorArray));
334
+ }
335
+ }
336
+
337
+ winrt::fire_and_forget ReactNativeBlobUtil::fetchBlob(
338
+ ::React::JSValue options,
339
+ std::string taskId,
340
+ std::string method,
341
+ std::string url,
342
+ ::React::JSValue headers,
343
+ std::string body,
344
+ std::function<void(::React::JSValueArray)> callback
345
+ ) noexcept
346
+ {
347
+ // Convert rvalue references to lvalues for safe use
348
+ ::React::JSValue& optionsRef = options;
349
+ ::React::JSValue& headersRef = headers;
350
+
351
+ try
352
+ {
353
+ winrt::Windows::Web::Http::Filters::HttpBaseProtocolFilter filter;
354
+ ReactNativeBlobUtilConfig config{ optionsRef };
355
+ filter.AllowAutoRedirect(false);
356
+ if (config.trusty)
357
+ {
358
+ filter.IgnorableServerCertificateErrors().Append(Cryptography::Certificates::ChainValidationResult::Untrusted);
359
+ }
360
+
361
+ winrt::Windows::Web::Http::HttpClient httpClient{ filter };
362
+
363
+ winrt::Windows::Web::Http::HttpMethod httpMethod{ winrt::Windows::Web::Http::HttpMethod::Post() };
364
+ if (method == "DELETE" || method == "delete")
365
+ {
366
+ httpMethod = winrt::Windows::Web::Http::HttpMethod::Delete();
367
+ }
368
+ else if (method == "PUT" || method == "put")
369
+ {
370
+ httpMethod = winrt::Windows::Web::Http::HttpMethod::Put();
371
+ }
372
+ else if (method == "GET" || method == "get")
373
+ {
374
+ httpMethod = winrt::Windows::Web::Http::HttpMethod::Get();
375
+ }
376
+ else
377
+ {
378
+ ::React::JSValueArray errorArray;
379
+ errorArray.push_back("Method not supported");
380
+ callback(std::move(errorArray));
381
+ co_return;
382
+ }
383
+
384
+ winrt::Windows::Web::Http::HttpRequestMessage requestMessage{
385
+ httpMethod,
386
+ winrt::Windows::Foundation::Uri{ winrt::to_hstring(url) }
387
+ };
388
+
389
+ std::string prefix = "file://";
390
+ bool pathToFile = body.rfind(prefix, 0) == 0;
391
+ if (pathToFile)
392
+ {
393
+ std::string contentPath = body.substr(prefix.length());
394
+ size_t fileLength = contentPath.length();
395
+ bool hasTrailingSlash = contentPath[fileLength - 1] == '\\' || contentPath[fileLength - 1] == '/';
396
+ winrt::hstring directoryPath, fileName;
397
+ splitPath(hasTrailingSlash ? contentPath.substr(0, fileLength - 1) : contentPath, directoryPath, fileName);
398
+ auto folder = co_await winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(directoryPath);
399
+ auto storageFile = co_await folder.GetFileAsync(fileName);
400
+ auto requestBuffer = co_await winrt::Windows::Storage::FileIO::ReadBufferAsync(storageFile);
401
+
402
+ winrt::Windows::Web::Http::HttpBufferContent requestContent{ requestBuffer };
403
+
404
+ for (const auto& entry : headersRef.AsObject())
405
+ {
406
+ if (!requestMessage.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString())))
407
+ {
408
+ requestContent.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString()));
409
+ }
410
+ }
411
+ requestMessage.Content(requestContent);
412
+ }
413
+ else if (!body.empty()) {
414
+ winrt::Windows::Web::Http::HttpStringContent requestString{ winrt::to_hstring(body) };
415
+
416
+ for (const auto& entry : headersRef.AsObject())
417
+ {
418
+ if (!requestMessage.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString())))
419
+ {
420
+ requestString.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString()));
421
+ }
422
+ }
423
+ requestMessage.Content(requestString);
424
+ }
425
+ else {
426
+ for (const auto& entry : headersRef.AsObject())
427
+ {
428
+ requestMessage.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString()));
429
+ }
430
+ }
431
+
432
+ // Send the request
433
+ auto response = co_await httpClient.SendRequestAsync(requestMessage);
434
+
435
+ std::string responseBody;
436
+ if (response.Content() != nullptr)
437
+ {
438
+ responseBody = winrt::to_string(co_await response.Content().ReadAsStringAsync());
439
+ }
440
+
441
+ ::React::JSValueArray resultArray;
442
+ resultArray.push_back(responseBody);
443
+ callback(std::move(resultArray));
444
+ }
445
+ catch (const winrt::hresult_error& ex)
446
+ {
447
+ ::React::JSValueArray errorArray;
448
+ errorArray.push_back("EUNSPECIFIED");
449
+ errorArray.push_back(winrt::to_string(ex.message()));
450
+ callback(std::move(errorArray));
451
+ }
452
+ catch (...)
453
+ {
454
+ ::React::JSValueArray errorArray;
455
+ errorArray.push_back("EUNSPECIFIED");
456
+ errorArray.push_back("Unknown error in fetchBlob");
457
+ callback(std::move(errorArray));
458
+ }
459
+ }
460
+
461
+ winrt::fire_and_forget ReactNativeBlobUtil::createFile(
462
+ std::string path,
463
+ std::wstring content,
464
+ std::string encoding,
465
+ winrt::Microsoft::ReactNative::ReactPromise<void> promise) noexcept
466
+ {
467
+ try
468
+ {
469
+ bool shouldExit{ false };
470
+ Streams::IBuffer buffer;
471
+ if (encoding.compare("uri") == 0)
472
+ {
473
+ try
474
+ {
475
+ winrt::hstring srcDirectoryPath, srcFileName;
476
+ splitPath(content, srcDirectoryPath, srcFileName);
477
+ StorageFolder srcFolder{ co_await StorageFolder::GetFolderFromPathAsync(srcDirectoryPath) };
478
+ StorageFile srcFile{ co_await srcFolder.GetFileAsync(srcFileName) };
479
+ buffer = co_await FileIO::ReadBufferAsync(srcFile);
480
+ }
481
+ catch (...)
482
+ {
483
+ shouldExit = true;
484
+ }
485
+ }
486
+ else if (encoding.compare("utf8") == 0)
487
+ {
488
+ buffer = CryptographicBuffer::ConvertStringToBinary(content, BinaryStringEncoding::Utf8);
489
+ }
490
+ else if (encoding.compare("base64") == 0)
491
+ {
492
+ buffer = CryptographicBuffer::DecodeFromBase64String(content);
493
+ }
494
+ else
495
+ {
496
+ promise.Reject("Invalid encoding");
497
+ shouldExit = true;
498
+ }
499
+
500
+ if (!shouldExit)
501
+ {
502
+
503
+ winrt::hstring destDirectoryPath, destFileName;
504
+ splitPath(path, destDirectoryPath, destFileName);
505
+
506
+ auto folder{ co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath) };
507
+
508
+ try
509
+ {
510
+ auto file{ co_await folder.CreateFileAsync(destFileName, CreationCollisionOption::FailIfExists) };
511
+ auto stream{ co_await file.OpenAsync(FileAccessMode::ReadWrite) };
512
+ co_await stream.WriteAsync(buffer);
513
+ }
514
+ catch (...)
515
+ {
516
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EEXIST", "EEXIST: File already exists; " + path });
517
+ shouldExit = true;
518
+ }
519
+ }
520
+ if (!shouldExit)
521
+ {
522
+ promise.Resolve();
523
+ }
524
+ co_return;
525
+ }
526
+ catch (const hresult_error& ex)
527
+ {
528
+ hresult result{ ex.code() };
529
+ if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) // FileNotFoundException
530
+ {
531
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "ENOENT", "ENOENT: File does not exist and could not be created; " + path });
532
+ }
533
+ else
534
+ {
535
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "EUNSPECIFIED: " + winrt::to_string(ex.message()) + "; " + path });
536
+ }
537
+ }
538
+ }
539
+
540
+ winrt::fire_and_forget ReactNativeBlobUtil::createFileASCII(
541
+ std::string path,
542
+ winrt::Microsoft::ReactNative::JSValueArray dataArray,
543
+ winrt::Microsoft::ReactNative::ReactPromise<void> promise) noexcept {
544
+ try
545
+ {
546
+ std::vector<uint8_t> data;
547
+ data.reserve(dataArray.size());
548
+ for (auto& var : dataArray)
549
+ {
550
+ data.push_back(var.AsUInt8());
551
+ }
552
+
553
+ Streams::IBuffer buffer{ CryptographicBuffer::CreateFromByteArray(data) };
554
+
555
+ winrt::hstring directoryPath, fileName;
556
+ splitPath(path, directoryPath, fileName);
557
+
558
+ StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
559
+
560
+ StorageFile file{ co_await folder.CreateFileAsync(fileName, CreationCollisionOption::FailIfExists) };
561
+ Streams::IRandomAccessStream stream{ co_await file.OpenAsync(FileAccessMode::ReadWrite) };
562
+ co_await stream.WriteAsync(buffer);
563
+
564
+ promise.Resolve();
565
+ }
566
+ catch (const hresult_error& ex)
567
+ {
568
+ hresult result{ ex.code() };
569
+ if (result == 0x80070002) // FileNotFoundException
570
+ {
571
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "ENOENT", "ENOENT: File does not exist and could not be created; " + path });
572
+ }
573
+ else if (result == 0x80070050)
574
+ {
575
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EEXIST", "EEXIST: File already exists; " + path });
576
+ }
577
+ else
578
+ {
579
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "EUNSPECIFIED: " + winrt::to_string(ex.message()) });
580
+ }
581
+ }
582
+ }
583
+
584
+ winrt::fire_and_forget ReactNativeBlobUtil::writeFile(
585
+ std::string path,
586
+ std::string encoding,
587
+ std::wstring data,
588
+ bool transformFile,
589
+ bool append,
590
+ winrt::Microsoft::ReactNative::ReactPromise<double> promise) noexcept
591
+ {
592
+ try
593
+ {
594
+ Streams::IBuffer buffer;
595
+ if (encoding.compare("utf8") == 0)
596
+ {
597
+ buffer = Cryptography::CryptographicBuffer::ConvertStringToBinary(data, BinaryStringEncoding::Utf8);
598
+ }
599
+ else if (encoding.compare("base64") == 0)
600
+ {
601
+ buffer = Cryptography::CryptographicBuffer::DecodeFromBase64String(data);
602
+ }
603
+ else if (encoding.compare("uri") == 0)
604
+ {
605
+ winrt::hstring srcDirectoryPath, srcFileName;
606
+ splitPath(data, srcDirectoryPath, srcFileName);
607
+ StorageFolder srcFolder{ co_await StorageFolder::GetFolderFromPathAsync(srcDirectoryPath) };
608
+ StorageFile srcFile{ co_await srcFolder.GetFileAsync(srcFileName) };
609
+ buffer = co_await FileIO::ReadBufferAsync(srcFile);
610
+ }
611
+ else
612
+ {
613
+ auto errorMessage{ "Invalid encoding: " + encoding };
614
+ promise.Reject(errorMessage.c_str());
615
+ }
616
+
617
+ winrt::hstring destDirectoryPath, destFileName;
618
+ splitPath(path, destDirectoryPath, destFileName);
619
+ StorageFolder destFolder{ co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath) };
620
+ StorageFile destFile{ nullptr };
621
+ if (append)
622
+ {
623
+ destFile = co_await destFolder.CreateFileAsync(destFileName, CreationCollisionOption::OpenIfExists);
624
+ }
625
+ else
626
+ {
627
+ destFile = co_await destFolder.CreateFileAsync(destFileName, CreationCollisionOption::ReplaceExisting);
628
+ }
629
+ Streams::IRandomAccessStream stream{ co_await destFile.OpenAsync(FileAccessMode::ReadWrite) };
630
+
631
+ if (append)
632
+ {
633
+ stream.Seek(UINT64_MAX);
634
+ }
635
+ co_await stream.WriteAsync(buffer);
636
+ promise.Resolve(static_cast<double>(buffer.Length()));
637
+ }
638
+ catch (const hresult_error& ex)
639
+ {
640
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "EUNSPECIFIED: " + winrt::to_string(ex.message()) + "; " + path });
641
+ }
642
+ }
643
+
644
+ winrt::fire_and_forget ReactNativeBlobUtil::writeFileArray(
645
+ std::string path,
646
+ winrt::Microsoft::ReactNative::JSValueArray dataArray,
647
+ bool append,
648
+ winrt::Microsoft::ReactNative::ReactPromise<double> promise) noexcept
649
+ {
650
+ try
651
+ {
652
+ std::vector<uint8_t> data;
653
+ data.reserve(dataArray.size());
654
+ for (auto& var : dataArray)
655
+ {
656
+ data.push_back(var.AsUInt8());
657
+ }
658
+ Streams::IBuffer buffer{ CryptographicBuffer::CreateFromByteArray(data) };
659
+
660
+ winrt::hstring destDirectoryPath, destFileName;
661
+ splitPath(path, destDirectoryPath, destFileName);
662
+ StorageFolder destFolder{ co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath) };
663
+ StorageFile destFile{ nullptr };
664
+ if (append)
665
+ {
666
+ destFile = co_await destFolder.CreateFileAsync(destFileName, CreationCollisionOption::OpenIfExists);
667
+ }
668
+ else
669
+ {
670
+ destFile = co_await destFolder.CreateFileAsync(destFileName, CreationCollisionOption::ReplaceExisting);
671
+ }
672
+ Streams::IRandomAccessStream stream{ co_await destFile.OpenAsync(FileAccessMode::ReadWrite) };
673
+
674
+ if (append)
675
+ {
676
+ stream.Seek(UINT64_MAX);
677
+ }
678
+ co_await stream.WriteAsync(buffer);
679
+ promise.Resolve(static_cast<double>(buffer.Length()));
680
+
681
+ co_return;
682
+ }
683
+ catch (const hresult_error& ex)
684
+ {
685
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "EUNSPECIFIED: " + winrt::to_string(ex.message()) + "; " + path });
686
+ }
687
+ }
688
+
689
+ void ReactNativeBlobUtil::pathForAppGroup(
690
+ std::string groupName,
691
+ ::React::ReactPromise<std::string>&& result) noexcept
283
692
  {
284
- hresult result{ ex.code() };
285
- if (result == 0x80070002) // FileNotFoundException
286
- {
287
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "ENOENT", "ENOENT: File does not exist and could not be created; " + path });
288
- }
289
- else if (result == 0x80070050)
290
- {
291
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EEXIST", "EEXIST: File already exists; " + path });
292
- }
293
- else
294
- {
295
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "EUNSPECIFIED: " + winrt::to_string(ex.message()) });
296
- }
693
+ result.Resolve("");
297
694
  }
298
695
 
299
-
300
- // writeFile
301
- winrt::fire_and_forget ReactNativeBlobUtil::writeFile(
302
- std::string path,
303
- std::string encoding,
304
- std::wstring data,
305
- bool append,
306
- winrt::Microsoft::ReactNative::ReactPromise<int> promise) noexcept
307
- try
308
- {
309
- Streams::IBuffer buffer;
310
- if (encoding.compare("utf8") == 0)
311
- {
312
- buffer = Cryptography::CryptographicBuffer::ConvertStringToBinary(data, BinaryStringEncoding::Utf8);
313
- }
314
- else if (encoding.compare("base64") == 0)
315
- {
316
- buffer = Cryptography::CryptographicBuffer::DecodeFromBase64String(data);
317
- }
318
- else if (encoding.compare("uri") == 0)
319
- {
320
- winrt::hstring srcDirectoryPath, srcFileName;
321
- splitPath(data, srcDirectoryPath, srcFileName);
322
- StorageFolder srcFolder{ co_await StorageFolder::GetFolderFromPathAsync(srcDirectoryPath) };
323
- StorageFile srcFile{ co_await srcFolder.GetFileAsync(srcFileName) };
324
- buffer = co_await FileIO::ReadBufferAsync(srcFile);
325
- }
326
- else
327
- {
328
- auto errorMessage{ "Invalid encoding: " + encoding };
329
- promise.Reject(errorMessage.c_str());
330
- }
331
-
332
- winrt::hstring destDirectoryPath, destFileName;
333
- splitPath(path, destDirectoryPath, destFileName);
334
- StorageFolder destFolder{ co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath) };
335
- StorageFile destFile{ nullptr };
336
- if (append)
337
- {
338
- destFile = co_await destFolder.CreateFileAsync(destFileName, CreationCollisionOption::OpenIfExists);
339
- }
340
- else
341
- {
342
- destFile = co_await destFolder.CreateFileAsync(destFileName, CreationCollisionOption::ReplaceExisting);
343
- }
344
- Streams::IRandomAccessStream stream{ co_await destFile.OpenAsync(FileAccessMode::ReadWrite) };
345
-
346
- if (append)
347
- {
348
- stream.Seek(UINT64_MAX);
349
- }
350
- co_await stream.WriteAsync(buffer);
351
- promise.Resolve(buffer.Length());
352
- }
353
- catch (const hresult_error& ex)
696
+ std::string ReactNativeBlobUtil::syncPathAppGroup(
697
+ std::string groupName) noexcept
354
698
  {
355
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "EUNSPECIFIED: " + winrt::to_string(ex.message()) + "; " + path });
699
+ return "";
356
700
  }
357
701
 
358
- winrt::fire_and_forget ReactNativeBlobUtil::writeFileArray(
359
- std::string path,
360
- winrt::Microsoft::ReactNative::JSValueArray dataArray,
361
- bool append,
362
- winrt::Microsoft::ReactNative::ReactPromise<int> promise) noexcept
363
- try
702
+ void ReactNativeBlobUtil::exists(
703
+ std::string path,
704
+ std::function<void(std::vector<bool> const&)> const& callback) noexcept
364
705
  {
365
- std::vector<uint8_t> data;
366
- data.reserve(dataArray.size());
367
- for (auto& var : dataArray)
368
- {
369
- data.push_back(var.AsUInt8());
370
- }
371
- Streams::IBuffer buffer{ CryptographicBuffer::CreateFromByteArray(data) };
372
-
373
- winrt::hstring destDirectoryPath, destFileName;
374
- splitPath(path, destDirectoryPath, destFileName);
375
- StorageFolder destFolder{ co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath) };
376
- StorageFile destFile{ nullptr };
377
- if (append)
378
- {
379
- destFile = co_await destFolder.CreateFileAsync(destFileName, CreationCollisionOption::OpenIfExists);
380
- }
381
- else
382
- {
383
- destFile = co_await destFolder.CreateFileAsync(destFileName, CreationCollisionOption::ReplaceExisting);
384
- }
385
- Streams::IRandomAccessStream stream{ co_await destFile.OpenAsync(FileAccessMode::ReadWrite) };
386
-
387
- if (append)
388
- {
389
- stream.Seek(UINT64_MAX);
390
- }
391
- co_await stream.WriteAsync(buffer);
392
- promise.Resolve(buffer.Length());
706
+ try
707
+ {
708
+ std::filesystem::path fsPath(path);
709
+ bool doesExist = std::filesystem::exists(fsPath);
710
+ bool isDirectory = std::filesystem::is_directory(fsPath);
393
711
 
394
- co_return;
395
- }
396
- catch (const hresult_error& ex)
397
- {
398
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "EUNSPECIFIED: " + winrt::to_string(ex.message()) + "; " + path });
712
+ callback(std::vector<bool>{ doesExist, isDirectory });
713
+ }
714
+ catch (const std::exception&)
715
+ {
716
+ // If something goes wrong, return false, false
717
+ callback(std::vector<bool>{ false, false });
718
+ }
399
719
  }
400
720
 
401
-
402
- // writeStream
403
721
  winrt::fire_and_forget ReactNativeBlobUtil::writeStream(
404
- std::string path,
405
- std::string encoding,
406
- bool append,
407
- std::function<void(std::string, std::string, std::string)> callback) noexcept
408
- try
722
+ std::string path,
723
+ std::string encoding,
724
+ bool appendData,
725
+ std::function<void(::React::JSValueArray)> callback) noexcept
409
726
  {
410
- winrt::hstring directoryPath, fileName;
411
- splitPath(path, directoryPath, fileName);
412
- StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
413
- StorageFile file{ co_await folder.CreateFileAsync(fileName, CreationCollisionOption::OpenIfExists) };
414
- std::string temp{encoding};
415
- Streams::IRandomAccessStream stream{ co_await file.OpenAsync(FileAccessMode::ReadWrite) };
416
- if (append)
417
- {
418
- stream.Seek(stream.Size());
419
- }
420
- EncodingOptions encodingOption;
421
- if (encoding.compare("utf8") == 0)
422
- {
423
- encodingOption = EncodingOptions::UTF8;
424
- }
425
- else if (encoding.compare("base64") == 0)
426
- {
427
- encodingOption = EncodingOptions::BASE64;
428
- }
429
- else if (encoding.compare("ascii") == 0)
430
- {
431
- encodingOption = EncodingOptions::ASCII;
432
- }
433
- else
434
- {
435
- co_return;
436
- }
437
-
438
- // Define the length, in bytes, of the buffer.
439
- uint32_t length = 128;
440
- // Generate random data and copy it to a buffer.
441
- IBuffer buffer = Cryptography::CryptographicBuffer::GenerateRandom(length);
442
- // Encode the buffer to a hexadecimal string (for display).
443
- std::string streamId = winrt::to_string(Cryptography::CryptographicBuffer::EncodeToHexString(buffer));
444
-
445
- ReactNativeBlobUtilStream streamInstance{ stream, encodingOption };
446
- m_streamMap.try_emplace(streamId, streamInstance);
447
-
448
- callback("", "", streamId);
727
+ try
728
+ {
729
+ winrt::hstring directoryPath, fileName;
730
+ splitPath(path, directoryPath, fileName);
731
+ auto folder = co_await StorageFolder::GetFolderFromPathAsync(directoryPath);
732
+ auto file = co_await folder.CreateFileAsync(fileName, CreationCollisionOption::OpenIfExists);
733
+ auto stream = co_await file.OpenAsync(FileAccessMode::ReadWrite);
734
+ if (appendData)
735
+ {
736
+ stream.Seek(stream.Size());
737
+ }
738
+
739
+ EncodingOptions encodingOption;
740
+ if (encoding == "utf8")
741
+ {
742
+ encodingOption = EncodingOptions::UTF8;
743
+ }
744
+ else if (encoding == "base64")
745
+ {
746
+ encodingOption = EncodingOptions::BASE64;
747
+ }
748
+ else if (encoding == "ascii")
749
+ {
750
+ encodingOption = EncodingOptions::ASCII;
751
+ }
752
+ else
753
+ {
754
+ // Return error as JSValueArray
755
+ ::React::JSValueArray errorArray;
756
+ errorArray.push_back("EUNSPECIFIED");
757
+ errorArray.push_back("Invalid encoding: " + encoding);
758
+ errorArray.push_back("");
759
+ callback(std::move(errorArray));
760
+ co_return;
761
+ }
762
+
763
+ // Generate a random streamId
764
+ uint32_t length = 16;
765
+ IBuffer buffer = Cryptography::CryptographicBuffer::GenerateRandom(length);
766
+ std::string streamId = winrt::to_string(Cryptography::CryptographicBuffer::EncodeToHexString(buffer));
767
+
768
+ ReactNativeBlobUtilStream streamInstance{ stream, encodingOption };
769
+ m_streamMap.try_emplace(streamId, streamInstance);
770
+
771
+ // Return success as JSValueArray
772
+ ::React::JSValueArray resultArray;
773
+ resultArray.push_back(""); // no error
774
+ resultArray.push_back(""); // no message
775
+ resultArray.push_back(streamId);
776
+ callback(std::move(resultArray));
777
+ }
778
+ catch (const winrt::hresult_error& ex)
779
+ {
780
+ ::React::JSValueArray errorArray;
781
+ errorArray.push_back("EUNSPECIFIED");
782
+ errorArray.push_back("Failed to create write stream at path '" + path + "'; " + winrt::to_string(ex.message()));
783
+ errorArray.push_back("");
784
+ callback(std::move(errorArray));
785
+ }
449
786
  }
450
- catch (const hresult_error& ex)
787
+
788
+ void ReactNativeBlobUtil::writeArrayChunk(
789
+ std::string streamId,
790
+ ::React::JSValueArray&& dataArray,
791
+ std::function<void(::React::JSValueArray const&)> const& callback) noexcept
451
792
  {
452
- callback("EUNSPECIFIED", "Failed to create write stream at path '" + path + "'; " + winrt::to_string(ex.message().c_str()), "");
793
+ try
794
+ {
795
+ auto streamIt = m_streamMap.find(streamId);
796
+ if (streamIt == m_streamMap.end()) {
797
+ ::React::JSValueArray errorArray;
798
+ errorArray.push_back("EUNSPECIFIED");
799
+ errorArray.push_back("Stream not found for id: " + streamId);
800
+ callback(errorArray);
801
+ return;
802
+ }
803
+ auto& stream = streamIt->second;
804
+ std::vector<uint8_t> data;
805
+ data.reserve(dataArray.size());
806
+ for (auto& var : dataArray)
807
+ {
808
+ data.push_back(var.AsUInt8());
809
+ }
810
+ Streams::IBuffer buffer{ CryptographicBuffer::CreateFromByteArray(data) };
811
+
812
+ stream.streamInstance.WriteAsync(buffer).get(); // Calls it synchronously
813
+ ::React::JSValueArray resultArray;
814
+ callback(resultArray); // Success: empty array
815
+ }
816
+ catch (const winrt::hresult_error& ex)
817
+ {
818
+ ::React::JSValueArray errorArray;
819
+ errorArray.push_back("EUNSPECIFIED");
820
+ errorArray.push_back(winrt::to_string(ex.message()));
821
+ callback(errorArray);
822
+ }
453
823
  }
454
824
 
455
-
456
- // writeChunk
457
825
  void ReactNativeBlobUtil::writeChunk(
458
- std::string streamId,
459
- std::wstring data,
460
- std::function<void(std::string)> callback) noexcept
461
- try
462
- {
463
- auto stream{ m_streamMap.find(streamId)->second };
464
- Streams::IBuffer buffer;
465
- if (stream.encoding == EncodingOptions::UTF8)
466
- {
467
- buffer = Cryptography::CryptographicBuffer::ConvertStringToBinary(data, BinaryStringEncoding::Utf8);
468
- }
469
- else if (stream.encoding == EncodingOptions::BASE64)
470
- {
471
- buffer = Cryptography::CryptographicBuffer::DecodeFromBase64String(data);
472
- }
473
- else
474
- {
475
- callback("Invalid encoding type");
476
- return;
477
- }
478
- stream.streamInstance.WriteAsync(buffer).get(); //Calls it synchronously
479
- callback("");
480
- }
481
- catch (const hresult_error& ex)
826
+ std::string streamId,
827
+ std::string data,
828
+ std::function<void(::React::JSValueArray const&)> const& callback) noexcept
482
829
  {
483
- callback(winrt::to_string(ex.message().c_str()));
830
+ try
831
+ {
832
+ auto streamIt = m_streamMap.find(streamId);
833
+ if (streamIt == m_streamMap.end()) {
834
+ ::React::JSValueArray errorArray;
835
+ errorArray.push_back("EUNSPECIFIED");
836
+ errorArray.push_back("Stream not found for id: " + streamId);
837
+ callback(errorArray);
838
+ return;
839
+ }
840
+ auto& stream = streamIt->second;
841
+ Streams::IBuffer buffer;
842
+ if (stream.encoding == EncodingOptions::UTF8)
843
+ {
844
+ buffer = Cryptography::CryptographicBuffer::ConvertStringToBinary(
845
+ winrt::to_hstring(data), BinaryStringEncoding::Utf8);
846
+ }
847
+ else if (stream.encoding == EncodingOptions::BASE64)
848
+ {
849
+ buffer = Cryptography::CryptographicBuffer::DecodeFromBase64String(winrt::to_hstring(data));
850
+ }
851
+ else
852
+ {
853
+ ::React::JSValueArray errorArray;
854
+ errorArray.push_back("EUNSPECIFIED");
855
+ errorArray.push_back("Invalid encoding type");
856
+ callback(errorArray);
857
+ return;
858
+ }
859
+ stream.streamInstance.WriteAsync(buffer).get(); // Synchronous write
860
+ ::React::JSValueArray resultArray;
861
+ callback(resultArray); // Success: empty array
862
+ }
863
+ catch (const winrt::hresult_error& ex)
864
+ {
865
+ ::React::JSValueArray errorArray;
866
+ errorArray.push_back("EUNSPECIFIED");
867
+ errorArray.push_back(winrt::to_string(ex.message()));
868
+ callback(errorArray);
869
+ }
484
870
  }
485
871
 
486
- void ReactNativeBlobUtil::writeArrayChunk(
487
- std::string streamId,
488
- winrt::Microsoft::ReactNative::JSValueArray dataArray,
489
- std::function<void(std::string)> callback) noexcept
490
- try
491
- {
492
- auto stream{ m_streamMap.find(streamId)->second };
493
- std::vector<uint8_t> data;
494
- data.reserve(dataArray.size());
495
- for (auto& var : dataArray)
496
- {
497
- data.push_back(var.AsUInt8());
498
- }
499
- Streams::IBuffer buffer{ CryptographicBuffer::CreateFromByteArray(data) };
500
-
501
- stream.streamInstance.WriteAsync(buffer).get(); // Calls it synchronously
502
- callback("");
503
- }
504
- catch (const hresult_error& ex)
872
+ void ReactNativeBlobUtil::closeStream(
873
+ std::string streamId,
874
+ std::function<void(::React::JSValueArray const&)> const& callback) noexcept
505
875
  {
506
- callback(winrt::to_string(ex.message().c_str()));
876
+ ::React::JSValueArray resultArray;
877
+ try
878
+ {
879
+ auto it = m_streamMap.find(streamId);
880
+ if (it != m_streamMap.end()) {
881
+ it->second.streamInstance.Close();
882
+ m_streamMap.erase(it);
883
+ // Success: return empty array
884
+ callback(resultArray);
885
+ } else {
886
+ // Stream not found
887
+ resultArray.push_back("EUNSPECIFIED");
888
+ resultArray.push_back("Stream not found for id: " + streamId);
889
+ callback(resultArray);
890
+ }
891
+ }
892
+ catch (const winrt::hresult_error& ex)
893
+ {
894
+ resultArray.push_back("EUNSPECIFIED");
895
+ resultArray.push_back(winrt::to_string(ex.message()));
896
+ callback(resultArray);
897
+ }
507
898
  }
508
899
 
509
- // readStream - no promises, callbacks, only event emission
510
- void ReactNativeBlobUtil::readStream(
511
- std::string path,
512
- std::string encoding,
513
- uint32_t bufferSize,
514
- uint64_t tick,
515
- const std::string streamId) noexcept
516
- try
900
+ winrt::fire_and_forget ReactNativeBlobUtil::unlink(
901
+ std::string path,
902
+ std::function<void(::React::JSValueArray)> callback) noexcept
517
903
  {
518
- EncodingOptions usedEncoding;
519
- if (encoding.compare("utf8") == 0)
520
- {
521
- usedEncoding = EncodingOptions::UTF8;
522
- }
523
- else if (encoding.compare("base64") == 0)
524
- {
525
- usedEncoding = EncodingOptions::BASE64;
526
- }
527
- else if (encoding.compare("ascii") == 0)
528
- {
529
- usedEncoding = EncodingOptions::ASCII;
530
- }
531
- else
532
- {
533
- //Wrong encoding
534
- return;
535
- }
536
-
537
- uint32_t chunkSize{ usedEncoding == EncodingOptions::BASE64 ? (uint32_t)4095 : (uint32_t)4096 };
538
- if (bufferSize > 0)
539
- {
540
- chunkSize = bufferSize;
541
- }
542
-
543
- winrt::hstring directoryPath, fileName;
544
- splitPath(path, directoryPath, fileName);
545
- StorageFolder folder{ StorageFolder::GetFolderFromPathAsync(directoryPath).get() };
546
- StorageFile file{ folder.GetFileAsync(fileName).get() };
547
-
548
- Streams::IRandomAccessStream stream{ file.OpenAsync(FileAccessMode::Read).get() };
549
- Buffer buffer{ chunkSize };
550
- const TimeSpan time{ tick };
551
- IAsyncAction timer;
552
-
553
- for (;;)
554
- {
555
- auto readBuffer = stream.ReadAsync(buffer, buffer.Capacity(), InputStreamOptions::None).get();
556
- if (readBuffer.Length() == 0)
557
- {
558
- m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", streamId,
559
- winrt::Microsoft::ReactNative::JSValueObject{
560
- {"event", "end"},
561
- });
562
- break;
563
- }
564
- if (usedEncoding == EncodingOptions::BASE64)
565
- {
566
- // TODO: Investigate returning wstrings as parameters
567
- winrt::hstring base64Content{ Cryptography::CryptographicBuffer::EncodeToBase64String(readBuffer) };
568
- //m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", [&streamId, &base64Content](React::IJSValueWriter const& argWriter) {
569
- // argWriter.WriteArrayBegin();
570
- // WriteValue(argWriter, streamId);
571
- // argWriter.WriteObjectBegin();
572
- // React::WriteProperty(argWriter, "event", "data");
573
- // React::WriteProperty(argWriter, "detail", base64Content);
574
- // argWriter.WriteObjectEnd();
575
- // argWriter.WriteArrayEnd();
576
- // });
577
- m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", streamId,
578
- winrt::Microsoft::ReactNative::JSValueObject{
579
- {"event", "data"},
580
- {"detail", winrt::to_string(base64Content)},
581
- });
582
- }
583
- else
584
- {
585
- // TODO: Sending events not working as necessary with writers
586
- std::string utf8Content{ winrt::to_string(Cryptography::CryptographicBuffer::ConvertBinaryToString(BinaryStringEncoding::Utf8, readBuffer)) };
587
- if (usedEncoding == EncodingOptions::ASCII)
588
- {
589
-
590
- //std::string asciiContent{ winrt::to_string(utf8Content) };
591
- std::string asciiContent{ utf8Content };
592
- // emit ascii content
593
- m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", streamId,
594
- winrt::Microsoft::ReactNative::JSValueObject{
595
- {"event", "data"},
596
- {"detail", asciiContent},
597
- });
598
- }
599
- else
600
- {
601
- //emit utf8 content
602
- m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", streamId,
603
- winrt::Microsoft::ReactNative::JSValueObject{
604
- {"event", "data"},
605
- {"detail", utf8Content},
606
- });
607
- //m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", [&streamId, &utf8Content](React::IJSValueWriter const& argWriter) {
608
- // WriteValue(argWriter, streamId);
609
- // argWriter.WriteObjectBegin();
610
- // React::WriteProperty(argWriter, "event", L"data");
611
- // React::WriteProperty(argWriter, "detail", utf8Content);
612
- // argWriter.WriteObjectEnd();
613
- // });
614
- }
615
- }
616
- // sleep
617
- if (tick > 0)
618
- {
619
- std::this_thread::sleep_for(std::chrono::milliseconds(tick));
620
- }
621
- }
622
- }
623
- catch (const hresult_error& ex)
904
+ try
905
+ {
906
+ if (std::filesystem::is_directory(path))
907
+ {
908
+ std::filesystem::path unlinkPath(path);
909
+ unlinkPath.make_preferred();
910
+ auto folderOp = winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(
911
+ winrt::to_hstring(unlinkPath.c_str()));
912
+ co_await folderOp.get().DeleteAsync();
913
+ }
914
+ else
915
+ {
916
+ winrt::hstring directoryPath, fileName;
917
+ splitPath(path, directoryPath, fileName);
918
+ auto folder = co_await winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(directoryPath);
919
+ auto item = co_await folder.GetItemAsync(fileName);
920
+ co_await item.DeleteAsync();
921
+ }
922
+
923
+ ::React::JSValueArray result;
924
+ result.push_back(::React::JSValue(true));
925
+ callback(std::move(result));
926
+ }
927
+ catch (const winrt::hresult_error& ex)
928
+ {
929
+ ::React::JSValueArray errorResult;
930
+ errorResult.push_back(::React::JSValue(false));
931
+ errorResult.push_back(::React::JSValue(winrt::to_string(ex.message())));
932
+ callback(std::move(errorResult));
933
+ }
934
+
935
+ }
936
+
937
+ winrt::fire_and_forget ReactNativeBlobUtil::removeSession(::React::JSValueArray paths, std::function<void(::React::JSValueArray)> callback) noexcept
624
938
  {
625
- hresult result{ ex.code() };
626
- if (result == 0x80070002) // FileNotFoundException
627
- {
628
- m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", streamId,
629
- winrt::Microsoft::ReactNative::JSValueObject{
630
- {"event", "error"},
631
- {"ENOENT", "No such file '" + path + "'"},
632
- });
633
- }
634
- else
635
- {
636
- m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", streamId,
637
- winrt::Microsoft::ReactNative::JSValueObject{
638
- {"event", "error"},
639
- {"EUNSPECIFIED", winrt::to_string(ex.message())},
640
- });
641
- }
939
+ try
940
+ {
941
+ for (const auto& pathValue : paths)
942
+ {
943
+ if (pathValue)
944
+ {
945
+ std::string path = pathValue.AsString();
946
+ auto folder = co_await winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(winrt::to_hstring(path));
947
+ auto file = co_await folder.GetFileAsync(winrt::to_hstring(path));
948
+ co_await file.DeleteAsync();
949
+ }
950
+ }
951
+
952
+ ::React::JSValueArray resultArray;
953
+ resultArray.push_back("SUCCESS");
954
+ callback(std::move(resultArray));
955
+ }
956
+ catch (const winrt::hresult_error& ex)
957
+ {
958
+ ::React::JSValueArray errorArray;
959
+ errorArray.push_back("ERROR");
960
+ errorArray.push_back(winrt::to_string(ex.message()));
961
+ callback(std::move(errorArray));
962
+ }
963
+ catch (...)
964
+ {
965
+ ::React::JSValueArray errorArray;
966
+ errorArray.push_back("ERROR");
967
+ errorArray.push_back("Unknown error in removeSession");
968
+ callback(std::move(errorArray));
969
+ }
642
970
  }
643
971
 
644
-
645
- // mkdir
646
- void ReactNativeBlobUtil::mkdir(
647
- std::string path,
648
- winrt::Microsoft::ReactNative::ReactPromise<bool> promise) noexcept
649
- try
650
- {
651
- std::filesystem::path dirPath(path);
652
- dirPath.make_preferred();
653
-
654
- // Consistent with Apple's createDirectoryAtPath method and result, but not with Android's
655
- if (std::filesystem::create_directories(dirPath) == false)
656
- {
657
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "ENOENT", "ENOENT: no such file or directory, open " + path });
658
- }
659
- else
660
- {
661
- promise.Resolve(true);
662
- }
663
- }
664
- catch (const hresult_error& ex)
972
+ winrt::fire_and_forget ReactNativeBlobUtil::ls(
973
+ std::string path,
974
+ ::React::ReactPromise<::React::JSValueArray> promise) noexcept
665
975
  {
666
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "Error creating folder " + path + ", error: " + winrt::to_string(ex.message().c_str()) });
976
+ try
977
+ {
978
+ winrt::hstring directoryPath, fileName;
979
+ splitPath(path, directoryPath, fileName);
980
+
981
+ auto folder = co_await Windows::Storage::StorageFolder::GetFolderFromPathAsync(directoryPath);
982
+ auto items = co_await folder.GetItemsAsync();
983
+
984
+ ::React::JSValueArray results;
985
+ for (const auto& item : items)
986
+ {
987
+ results.push_back(::React::JSValue{ winrt::to_string(item.Name()) });
988
+ }
989
+
990
+ promise.Resolve(results);
991
+ }
992
+ catch (const winrt::hresult_error& ex)
993
+ {
994
+ hresult result = ex.code();
995
+ if (result == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))
996
+ {
997
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "ENOTDIR", "Not a directory '" + path + "'" });
998
+ }
999
+ else
1000
+ {
1001
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", winrt::to_string(ex.message()) });
1002
+ }
1003
+ }
667
1004
  }
668
1005
 
669
-
670
- // readFile - TODO: try returning wchar array and investigate different return types
671
- winrt::fire_and_forget ReactNativeBlobUtil::readFile(
672
- std::string path,
673
- std::string encoding,
674
- winrt::Microsoft::ReactNative::ReactPromise<std::wstring> promise) noexcept
675
- try
676
- {
677
- winrt::hstring directoryPath, fileName;
678
- splitPath(path, directoryPath, fileName);
679
-
680
- StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
681
- StorageFile file{ co_await folder.GetFileAsync(fileName) };
682
-
683
- Streams::IBuffer buffer{ co_await FileIO::ReadBufferAsync(file) };
684
- if (encoding.compare("base64") == 0)
685
- {
686
- std::wstring base64Content{ Cryptography::CryptographicBuffer::EncodeToBase64String(buffer) };
687
- promise.Resolve(base64Content);
688
- }
689
- else
690
- {
691
- std::wstring utf8Content{ Cryptography::CryptographicBuffer::ConvertBinaryToString(BinaryStringEncoding::Utf8, buffer) };
692
- if (encoding.compare("ascii") == 0)
693
- {
694
- std::string asciiContent{ winrt::to_string(utf8Content) };
695
- std::wstring asciiResult{ winrt::to_hstring(asciiContent) };
696
- promise.Resolve(asciiResult);
697
- co_return;
698
- }
699
- promise.Resolve(utf8Content);
700
- }
701
- }
702
- catch (const hresult_error& ex)
1006
+ winrt::fire_and_forget ReactNativeBlobUtil::stat(
1007
+ std::string path,
1008
+ std::function<void(::React::JSValueArray)> callback) noexcept
703
1009
  {
704
- hresult result{ ex.code() };
705
- if (result == 0x80070002) // FileNotFoundException
706
- {
707
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "ENOENT", "ENOENT: no such file or directory, open " + path });
708
- }
709
- else if (result == 0x80070005) // UnauthorizedAccessException
710
- {
711
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EISDIR", "EISDIR: illegal operation on a directory, read" });
712
- }
713
- else
714
- {
715
- // "Failed to read file."
716
- promise.Reject(winrt::to_string(ex.message()).c_str());
717
- }
1010
+ try
1011
+ {
1012
+ std::filesystem::path givenPath(path);
1013
+ givenPath.make_preferred();
1014
+ bool isDirectory{ std::filesystem::is_directory(path) };
1015
+
1016
+ //std::string resultPath{ winrt::to_string(givenPath.c_str()) };
1017
+ auto resultPath{ winrt::to_hstring(givenPath.c_str()) };
1018
+
1019
+ // Try to open as folder
1020
+ IStorageItem item;
1021
+ if (isDirectory) {
1022
+ item = co_await StorageFolder::GetFolderFromPathAsync(resultPath);
1023
+ }
1024
+ else {
1025
+ item = co_await StorageFile::GetFileFromPathAsync(resultPath);
1026
+ }
1027
+ auto properties{ co_await item.GetBasicPropertiesAsync() };
1028
+ winrt::Microsoft::ReactNative::JSValueObject fileInfo;
1029
+ fileInfo["size"] = properties.Size();
1030
+ fileInfo["filename"] = givenPath.filename().string();
1031
+ fileInfo["path"] = givenPath.string();
1032
+ fileInfo["lastModified"] = winrt::clock::to_time_t(properties.DateModified());;
1033
+ fileInfo["type"] = isDirectory ? "directory" : "file";
1034
+
1035
+ ::React::JSValueArray result;
1036
+ result.push_back(std::move(fileInfo));
1037
+ callback(std::move(result));
1038
+ }
1039
+ catch (const hresult_error& ex)
1040
+ {
1041
+ ::React::JSValueArray errorArray;
1042
+ ::React::JSValueObject error;
1043
+ error["error"] = winrt::to_string(ex.message());
1044
+ errorArray.push_back(std::move(error));
1045
+ callback(std::move(errorArray));
1046
+ }
718
1047
  }
719
1048
 
720
-
721
- // hash
722
- winrt::fire_and_forget ReactNativeBlobUtil::hash(
723
- std::string path,
724
- std::string algorithm,
725
- winrt::Microsoft::ReactNative::ReactPromise<std::wstring> promise) noexcept
726
- try
1049
+ winrt::fire_and_forget ReactNativeBlobUtil::lstat(
1050
+ std::string path,
1051
+ std::function<void(::React::JSValueArray)> callback) noexcept
727
1052
  {
728
- // Note: SHA224 is not part of winrt
729
- if (algorithm.compare("sha224") == 0)
730
- {
731
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "Error", "WinRT does not offer sha224 encryption." });
732
- co_return;
733
- }
1053
+ try
1054
+ {
1055
+ std::filesystem::path directory(path);
1056
+ directory.make_preferred();
1057
+ StorageFolder targetDirectory{ co_await StorageFolder::GetFolderFromPathAsync(directory.c_str()) };
734
1058
 
735
- winrt::hstring directoryPath, fileName;
736
- splitPath(path, directoryPath, fileName);
1059
+ winrt::Microsoft::ReactNative::JSValueArray resultsArray;
737
1060
 
738
- StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
739
- StorageFile file{ co_await folder.GetFileAsync(fileName) };
1061
+ auto items{ co_await targetDirectory.GetItemsAsync() };
1062
+ for (auto item : items)
1063
+ {
1064
+ auto properties{ co_await item.GetBasicPropertiesAsync() };
740
1065
 
741
- auto search{ availableHashes.find(algorithm) };
742
- if (search == availableHashes.end())
743
- {
744
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "Error", "Invalid hash algorithm " + algorithm });
745
- co_return;
746
- }
1066
+ winrt::Microsoft::ReactNative::JSValueObject itemInfo;
747
1067
 
748
- CryptographyCore::HashAlgorithmProvider provider{ search->second() };
749
- Streams::IBuffer buffer{ co_await FileIO::ReadBufferAsync(file) };
1068
+ itemInfo["filename"] = to_string(item.Name());
1069
+ itemInfo["path"] = to_string(item.Path());
1070
+ itemInfo["size"] = properties.Size();
1071
+ itemInfo["type"] = item.IsOfType(StorageItemTypes::Folder) ? "directory" : "file";
1072
+ itemInfo["lastModified"] = properties.DateModified().time_since_epoch() / std::chrono::seconds(1) - UNIX_EPOCH_IN_WINRT_SECONDS;
750
1073
 
751
- auto hashedBuffer{ provider.HashData(buffer) };
752
- std::wstring result{ Cryptography::CryptographicBuffer::EncodeToHexString(hashedBuffer) };
1074
+ resultsArray.push_back(std::move(itemInfo));
1075
+ }
753
1076
 
754
- promise.Resolve(result);
1077
+ callback(std::move(resultsArray));
1078
+ }
1079
+ catch (...)
1080
+ {
1081
+ // "Failed to read directory."
1082
+ winrt::Microsoft::ReactNative::JSValueArray emptyArray;
1083
+ callback(std::move(emptyArray));
1084
+ }
755
1085
  }
756
- catch (const hresult_error& ex)
1086
+
1087
+ winrt::fire_and_forget ReactNativeBlobUtil::cp(
1088
+ std::string src,
1089
+ std::string dest,
1090
+ std::function<void(::React::JSValueArray)> callback) noexcept
757
1091
  {
758
- hresult result{ ex.code() };
759
- if (result == 0x80070002) // FileNotFoundException
760
- {
761
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "ENOENT", "ENOENT: no such file or directory, open " + path });
762
- }
763
- else if (result == 0x80070005) // UnauthorizedAccessException
764
- {
765
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EISDIR", "EISDIR: illegal operation on a directory, read" });
766
- }
767
- else
768
- {
769
- // "Failed to get checksum from file."
770
- promise.Reject(winrt::to_string(ex.message()).c_str());
771
- }
772
- }
1092
+ try
1093
+ {
1094
+ winrt::hstring srcDirectoryPath, srcFileName;
1095
+ splitPath(src, srcDirectoryPath, srcFileName);
773
1096
 
1097
+ winrt::hstring destDirectoryPath, destFileName;
1098
+ splitPath(dest, destDirectoryPath, destFileName);
774
1099
 
775
- // ls
776
- winrt::fire_and_forget ReactNativeBlobUtil::ls(
777
- std::string path,
778
- winrt::Microsoft::ReactNative::ReactPromise<std::vector<std::string>> promise) noexcept
779
- try
780
- {
781
- winrt::hstring directoryPath, fileName;
782
- splitPath(path, directoryPath, fileName);
1100
+ StorageFolder srcFolder = co_await StorageFolder::GetFolderFromPathAsync(srcDirectoryPath);
1101
+ StorageFolder destFolder = co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath);
783
1102
 
784
- StorageFolder targetDirectory{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
1103
+ StorageFile file = co_await srcFolder.GetFileAsync(srcFileName);
1104
+ co_await file.CopyAsync(destFolder, destFileName, NameCollisionOption::FailIfExists);
785
1105
 
786
- std::vector<std::string> results;
787
- auto items{ co_await targetDirectory.GetItemsAsync() };
788
- for (auto item : items)
789
- {
790
- results.push_back(to_string(item.Name()));
791
- }
792
- promise.Resolve(results);
793
- }
794
- catch (const hresult_error& ex)
795
- {
796
- hresult result{ ex.code() };
797
- if (result == 0x80070002) // FileNotFoundException
798
- {
799
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "ENOTDIR", "Not a directory '" + path + "'" });
800
- }
801
- else
802
- {
803
- promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", winrt::to_string(ex.message()).c_str() });
804
- }
1106
+ ::React::JSValueArray success{};
1107
+ callback(std::move(success));
1108
+ }
1109
+ catch (const winrt::hresult_error& ex)
1110
+ {
1111
+ ::React::JSValueArray error{ winrt::to_string(ex.message()) };
1112
+ callback(std::move(error));
1113
+ }
805
1114
  }
806
1115
 
807
-
808
- // mv
809
1116
  winrt::fire_and_forget ReactNativeBlobUtil::mv(
810
- std::string src, // from
811
- std::string dest, // to
812
- std::function<void(std::string)> callback) noexcept
1117
+ std::string src,
1118
+ std::string dest,
1119
+ std::function<void(::React::JSValueArray)> callback) noexcept
813
1120
  {
814
- try
815
- {
816
- winrt::hstring srcDirectoryPath, srcFileName;
817
- splitPath(src, srcDirectoryPath, srcFileName);
1121
+ try
1122
+ {
1123
+ winrt::hstring srcDirectoryPath, srcFileName;
1124
+ splitPath(src, srcDirectoryPath, srcFileName);
818
1125
 
819
- winrt::hstring destDirectoryPath, destFileName;
820
- splitPath(dest, destDirectoryPath, destFileName);
1126
+ winrt::hstring destDirectoryPath, destFileName;
1127
+ splitPath(dest, destDirectoryPath, destFileName);
821
1128
 
822
- StorageFolder srcFolder{ co_await StorageFolder::GetFolderFromPathAsync(srcDirectoryPath) };
823
- StorageFolder destFolder{ co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath) };
824
- StorageFile file{ co_await srcFolder.GetFileAsync(srcFileName) };
1129
+ StorageFolder srcFolder = co_await StorageFolder::GetFolderFromPathAsync(srcDirectoryPath);
1130
+ StorageFolder destFolder = co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath);
825
1131
 
826
- co_await file.MoveAsync(destFolder, destFileName, NameCollisionOption::ReplaceExisting);
827
- callback("");
828
- }
829
- catch (const hresult_error& ex)
830
- {
831
- hresult result{ ex.code() };
832
- if (result == 0x80070002) // FileNotFoundException
833
- {
834
- callback("Source file not found.");
835
- }
836
- else
837
- {
838
- callback(winrt::to_string(ex.message()).c_str());
839
- }
840
- }
841
- }
1132
+ StorageFile file = co_await srcFolder.GetFileAsync(srcFileName);
1133
+ co_await file.MoveAsync(destFolder, destFileName, NameCollisionOption::ReplaceExisting);
842
1134
 
1135
+ ::React::JSValueArray success{}; // Success: empty array
1136
+ callback(std::move(success));
1137
+ }
1138
+ catch (const winrt::hresult_error& ex)
1139
+ {
1140
+ ::React::JSValueArray error{ winrt::to_string(ex.message()) };
1141
+ callback(std::move(error));
1142
+ }
1143
+ }
843
1144
 
844
- // cp
845
- winrt::fire_and_forget ReactNativeBlobUtil::cp(
846
- std::string src, // from
847
- std::string dest, // to
848
- std::function<void(std::string)> callback) noexcept
849
- try
1145
+ void ReactNativeBlobUtil::mkdir(
1146
+ std::string path,
1147
+ ::React::ReactPromise<bool>&& promise) noexcept
850
1148
  {
851
- winrt::hstring srcDirectoryPath, srcFileName;
852
- splitPath(src, srcDirectoryPath, srcFileName);
853
-
854
- winrt::hstring destDirectoryPath, destFileName;
855
- splitPath(dest, destDirectoryPath, destFileName);
856
-
857
- StorageFolder srcFolder{ co_await StorageFolder::GetFolderFromPathAsync(srcDirectoryPath) };
858
- StorageFolder destFolder{ co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath) };
859
- StorageFile file{ co_await srcFolder.GetFileAsync(srcFileName) };
860
-
861
- co_await file.CopyAsync(destFolder, destFileName, NameCollisionOption::FailIfExists);
862
-
863
- callback("");
1149
+ try
1150
+ {
1151
+ std::filesystem::path dirPath(path);
1152
+ dirPath.make_preferred();
1153
+
1154
+ // Consistent with Apple's createDirectoryAtPath method and result, but not with Android's
1155
+ std::error_code ec;
1156
+ bool created = std::filesystem::create_directories(dirPath, ec);
1157
+ if (!created && !std::filesystem::exists(dirPath))
1158
+ {
1159
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "ENOENT", "ENOENT: no such file or directory, open " + path });
1160
+ }
1161
+ else
1162
+ {
1163
+ promise.Resolve(true);
1164
+ }
1165
+ }
1166
+ catch (const hresult_error& ex)
1167
+ {
1168
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "Error creating folder " + path + ", error: " + winrt::to_string(ex.message()) });
1169
+ }
864
1170
  }
865
- catch (const hresult_error& ex)
1171
+
1172
+ winrt::fire_and_forget ReactNativeBlobUtil::readFile(
1173
+ std::string path,
1174
+ std::string encoding,
1175
+ bool transformFile,
1176
+ ::React::ReactPromise<::React::JSValueArray> promise) noexcept
866
1177
  {
867
- hresult result{ ex.code() };
868
- if (result == 0x80070002) // FileNotFoundException
869
- {
870
- callback("Source file not found.");
871
- }
872
- callback(winrt::to_string(ex.message()).c_str());
1178
+ try
1179
+ {
1180
+ winrt::hstring directoryPath, fileName;
1181
+ splitPath(path, directoryPath, fileName);
1182
+
1183
+ auto folder = co_await StorageFolder::GetFolderFromPathAsync(directoryPath);
1184
+ auto file = co_await folder.GetFileAsync(fileName);
1185
+ auto buffer = co_await FileIO::ReadBufferAsync(file);
1186
+
1187
+ ::React::JSValueArray resultArray;
1188
+ if (encoding == "base64")
1189
+ {
1190
+ std::string base64Content = winrt::to_string(Cryptography::CryptographicBuffer::EncodeToBase64String(buffer));
1191
+ resultArray.push_back(base64Content);
1192
+ }
1193
+ else
1194
+ {
1195
+ std::string utf8Content = winrt::to_string(Cryptography::CryptographicBuffer::ConvertBinaryToString(BinaryStringEncoding::Utf8, buffer));
1196
+ if (encoding == "ascii")
1197
+ {
1198
+ // For ASCII, just return the utf8Content as a string
1199
+ resultArray.push_back(utf8Content);
1200
+ }
1201
+ else
1202
+ {
1203
+ resultArray.push_back(utf8Content);
1204
+ }
1205
+ }
1206
+ promise.Resolve(resultArray);
1207
+ }
1208
+ catch (const winrt::hresult_error& ex)
1209
+ {
1210
+ hresult result{ ex.code() };
1211
+ if (result == 0x80070002) // FileNotFoundException
1212
+ {
1213
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "ENOENT", "ENOENT: no such file or directory, open " + path });
1214
+ }
1215
+ else if (result == 0x80070005) // UnauthorizedAccessException
1216
+ {
1217
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EISDIR", "EISDIR: illegal operation on a directory, read" });
1218
+ }
1219
+ else
1220
+ {
1221
+ // "Failed to read file."
1222
+ promise.Reject(winrt::to_string(ex.message()).c_str());
1223
+ }
1224
+ }
873
1225
  }
874
1226
 
875
-
876
- // exists
877
- void ReactNativeBlobUtil::exists(
878
- std::string path,
879
- std::function<void(bool, bool)> callback) noexcept
1227
+ winrt::fire_and_forget ReactNativeBlobUtil::hash(
1228
+ std::string path,
1229
+ std::string algorithm,
1230
+ ::React::ReactPromise<std::string> promise) noexcept
880
1231
  {
881
- std::filesystem::path fsPath(path);
882
- bool doesExist{ std::filesystem::exists(fsPath) };
883
- bool isDirectory{ std::filesystem::is_directory(fsPath) };
884
-
885
- callback(doesExist, isDirectory);
1232
+ try
1233
+ {
1234
+ // Note: SHA224 is not part of winrt
1235
+ if (algorithm.compare("sha224") == 0)
1236
+ {
1237
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "Error", "WinRT does not offer sha224 encryption." });
1238
+ co_return;
1239
+ }
1240
+
1241
+ winrt::hstring directoryPath, fileName;
1242
+ splitPath(path, directoryPath, fileName);
1243
+
1244
+ StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
1245
+ StorageFile file{ co_await folder.GetFileAsync(fileName) };
1246
+
1247
+ auto search{ availableHashes.find(algorithm) };
1248
+ if (search == availableHashes.end())
1249
+ {
1250
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "Error", "Invalid hash algorithm " + algorithm });
1251
+ co_return;
1252
+ }
1253
+
1254
+ CryptographyCore::HashAlgorithmProvider provider{ search->second() };
1255
+ Streams::IBuffer buffer{ co_await FileIO::ReadBufferAsync(file) };
1256
+
1257
+ auto hashedBuffer{ provider.HashData(buffer) };
1258
+ std::wstring result{ Cryptography::CryptographicBuffer::EncodeToHexString(hashedBuffer) };
1259
+ std::string sResult = winrt::to_string(result);
1260
+ promise.Resolve(sResult);
1261
+ }
1262
+ catch (const hresult_error& ex)
1263
+ {
1264
+ hresult result{ ex.code() };
1265
+ if (result == 0x80070002) // FileNotFoundException
1266
+ {
1267
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "ENOENT", "ENOENT: no such file or directory, open " + path });
1268
+ }
1269
+ else if (result == 0x80070005) // UnauthorizedAccessException
1270
+ {
1271
+ promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EISDIR", "EISDIR: illegal operation on a directory, read" });
1272
+ }
1273
+ else
1274
+ {
1275
+ // "Failed to get checksum from file."
1276
+ promise.Reject(winrt::to_string(ex.message()).c_str());
1277
+ }
1278
+ }
1279
+ }
1280
+
1281
+ winrt::fire_and_forget ReactNativeBlobUtil::readStream(
1282
+ std::string path,
1283
+ std::string encoding,
1284
+ double bufferSize,
1285
+ double tick,
1286
+ std::string streamId) noexcept
1287
+ {
1288
+ try
1289
+ {
1290
+ EncodingOptions usedEncoding;
1291
+ if (encoding == "utf8")
1292
+ {
1293
+ usedEncoding = EncodingOptions::UTF8;
1294
+ }
1295
+ else if (encoding == "base64")
1296
+ {
1297
+ usedEncoding = EncodingOptions::BASE64;
1298
+ }
1299
+ else if (encoding == "ascii")
1300
+ {
1301
+ usedEncoding = EncodingOptions::ASCII;
1302
+ }
1303
+ else
1304
+ {
1305
+ // Invalid encoding
1306
+ m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", streamId,
1307
+ winrt::Microsoft::ReactNative::JSValueObject{
1308
+ {"event", "error"},
1309
+ {"EINVAL", "Unsupported encoding: " + encoding}
1310
+ });
1311
+ co_return;
1312
+ }
1313
+
1314
+ uint32_t chunkSize = (usedEncoding == EncodingOptions::BASE64) ? 4095 : 4096;
1315
+ if (bufferSize > 0)
1316
+ {
1317
+ chunkSize = static_cast<uint32_t>(bufferSize);
1318
+ }
1319
+
1320
+ winrt::hstring directoryPath, fileName;
1321
+ splitPath(path, directoryPath, fileName);
1322
+
1323
+ StorageFolder folder = co_await StorageFolder::GetFolderFromPathAsync(directoryPath);
1324
+ StorageFile file = co_await folder.GetFileAsync(fileName);
1325
+ Streams::IRandomAccessStream stream = co_await file.OpenAsync(FileAccessMode::Read);
1326
+
1327
+ Buffer buffer{ chunkSize };
1328
+
1329
+ for (;;)
1330
+ {
1331
+ auto readBuffer = co_await stream.ReadAsync(buffer, buffer.Capacity(), InputStreamOptions::None);
1332
+ if (readBuffer.Length() == 0)
1333
+ {
1334
+ m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", streamId,
1335
+ winrt::Microsoft::ReactNative::JSValueObject{
1336
+ {"event", "end"}
1337
+ });
1338
+ break;
1339
+ }
1340
+
1341
+ if (usedEncoding == EncodingOptions::BASE64)
1342
+ {
1343
+ winrt::hstring base64Content = Cryptography::CryptographicBuffer::EncodeToBase64String(readBuffer);
1344
+ m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", streamId,
1345
+ winrt::Microsoft::ReactNative::JSValueObject{
1346
+ {"event", "data"},
1347
+ {"detail", winrt::to_string(base64Content)}
1348
+ });
1349
+ }
1350
+ else
1351
+ {
1352
+ winrt::hstring stringContent = Cryptography::CryptographicBuffer::ConvertBinaryToString(
1353
+ BinaryStringEncoding::Utf8, readBuffer);
1354
+ std::string utf8Content = winrt::to_string(stringContent);
1355
+
1356
+ // For ASCII, trim to 7-bit range
1357
+ if (usedEncoding == EncodingOptions::ASCII)
1358
+ {
1359
+ for (char& c : utf8Content)
1360
+ {
1361
+ c &= 0x7F;
1362
+ }
1363
+ }
1364
+
1365
+ m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", streamId,
1366
+ winrt::Microsoft::ReactNative::JSValueObject{
1367
+ {"event", "data"},
1368
+ {"detail", utf8Content}
1369
+ });
1370
+ }
1371
+
1372
+ if (tick > 0)
1373
+ {
1374
+ std::this_thread::sleep_for(std::chrono::milliseconds(static_cast<int64_t>(tick)));
1375
+ }
1376
+ }
1377
+ }
1378
+ catch (const hresult_error& ex)
1379
+ {
1380
+ hresult result = ex.code();
1381
+ if (result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) // 0x80070002
1382
+ {
1383
+ m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", streamId,
1384
+ winrt::Microsoft::ReactNative::JSValueObject{
1385
+ {"event", "error"},
1386
+ {"ENOENT", "No such file: " + path}
1387
+ });
1388
+ }
1389
+ else
1390
+ {
1391
+ m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", streamId,
1392
+ winrt::Microsoft::ReactNative::JSValueObject{
1393
+ {"event", "error"},
1394
+ {"EUNSPECIFIED", winrt::to_string(ex.message())}
1395
+ });
1396
+ }
1397
+ }
1398
+ }
1399
+
1400
+ void ReactNativeBlobUtil::getEnvironmentDirs(
1401
+ std::function<void(::React::JSValueArray const&)> const& callback) noexcept
1402
+ {
1403
+ callback(::React::JSValueArray{});
886
1404
  }
887
1405
 
888
-
889
- // unlink
890
- winrt::fire_and_forget ReactNativeBlobUtil::unlink(
891
- std::string path,
892
- std::function<void(std::string, bool)> callback) noexcept
893
- try
1406
+ void ReactNativeBlobUtil::cancelRequest(
1407
+ std::string taskId,
1408
+ std::function<void(::React::JSValueArray const&)> const& callback) noexcept
894
1409
  {
895
- if (std::filesystem::is_directory(path))
896
- {
897
- std::filesystem::path unlinkPath(path);
898
- unlinkPath.make_preferred();
899
- StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(winrt::to_hstring(unlinkPath.c_str())) };
900
- co_await folder.DeleteAsync();
901
- }
902
- else
903
- {
904
- winrt::hstring directoryPath, fileName;
905
- splitPath(path, directoryPath, fileName);
906
- StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
907
- auto target{ co_await folder.GetItemAsync(fileName) };
908
- co_await target.DeleteAsync();
909
- }
910
- callback("", true);
1410
+ ::React::JSValueArray resultArray;
1411
+ try
1412
+ {
1413
+ m_tasks.Cancel(taskId);
1414
+ // Success: return empty array
1415
+ callback(resultArray);
1416
+ }
1417
+ catch (const winrt::hresult_error& ex)
1418
+ {
1419
+ resultArray.push_back("EUNSPECIFIED");
1420
+ resultArray.push_back(winrt::to_string(ex.message()));
1421
+ callback(resultArray);
1422
+ }
911
1423
  }
912
- catch (const hresult_error& ex)
1424
+
1425
+ void ReactNativeBlobUtil::enableProgressReport(
1426
+ std::string taskId,
1427
+ double interval,
1428
+ double count) noexcept
913
1429
  {
914
- callback(winrt::to_string(ex.message()), false);
1430
+ ReactNativeBlobUtilProgressConfig config{ count, interval };
1431
+ std::scoped_lock lock{ m_mutex };
1432
+ downloadProgressMap.try_emplace(taskId, config);
915
1433
  }
916
1434
 
917
-
918
- // lstat
919
- winrt::fire_and_forget ReactNativeBlobUtil::lstat(
920
- std::string path,
921
- std::function<void(std::string, winrt::Microsoft::ReactNative::JSValueArray&)> callback) noexcept
922
- try
1435
+ void ReactNativeBlobUtil::enableUploadProgressReport(
1436
+ std::string taskId,
1437
+ double interval,
1438
+ double count) noexcept
923
1439
  {
924
- std::filesystem::path directory(path);
925
- directory.make_preferred();
926
- StorageFolder targetDirectory{ co_await StorageFolder::GetFolderFromPathAsync(directory.c_str()) };
927
-
928
- winrt::Microsoft::ReactNative::JSValueArray resultsArray;
929
-
930
- auto items{ co_await targetDirectory.GetItemsAsync() };
931
- for (auto item : items)
932
- {
933
- auto properties{ co_await item.GetBasicPropertiesAsync() };
934
-
935
- winrt::Microsoft::ReactNative::JSValueObject itemInfo;
936
-
937
- itemInfo["filename"] = to_string(item.Name());
938
- itemInfo["path"] = to_string(item.Path());
939
- itemInfo["size"] = properties.Size();
940
- itemInfo["type"] = item.IsOfType(StorageItemTypes::Folder) ? "directory" : "file";
941
- itemInfo["lastModified"] = properties.DateModified().time_since_epoch() / std::chrono::seconds(1) - UNIX_EPOCH_IN_WINRT_SECONDS;
942
-
943
- resultsArray.push_back(std::move(itemInfo));
944
- }
945
-
946
- callback("", resultsArray);
1440
+ ReactNativeBlobUtilProgressConfig config{ count, interval };
1441
+ std::scoped_lock lock{ m_mutex };
1442
+ uploadProgressMap.try_emplace(taskId, config);
947
1443
  }
948
- catch (...)
1444
+
1445
+ winrt::fire_and_forget ReactNativeBlobUtil::slice(
1446
+ std::string src,
1447
+ std::string dest,
1448
+ double start,
1449
+ double end,
1450
+ ::React::ReactPromise<std::string> promise) noexcept
949
1451
  {
950
- // "Failed to read directory."
951
- winrt::Microsoft::ReactNative::JSValueArray emptyArray;
952
- callback("failed to lstat path `" + path + "` because it does not exist or it is not a folder", emptyArray);
1452
+ try
1453
+ {
1454
+ winrt::hstring srcDirectoryPath, srcFileName, destDirectoryPath, destFileName;
1455
+ splitPath(src, srcDirectoryPath, srcFileName);
1456
+ splitPath(src, destDirectoryPath, destFileName);
1457
+
1458
+ StorageFolder srcFolder{ co_await StorageFolder::GetFolderFromPathAsync(srcDirectoryPath) };
1459
+ StorageFolder destFolder{ co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath) };
1460
+
1461
+ StorageFile srcFile{ co_await srcFolder.GetFileAsync(srcFileName) };
1462
+ StorageFile destFile{ co_await destFolder.CreateFileAsync(destFileName, CreationCollisionOption::OpenIfExists) };
1463
+
1464
+ uint64_t uStart = static_cast<uint64_t>(start);
1465
+ uint64_t uEnd = static_cast<uint64_t>(end);
1466
+ uint32_t length = static_cast<uint32_t>(uEnd > uStart ? uEnd - uStart : 0);
1467
+
1468
+ if (length == 0) {
1469
+ promise.Reject("Invalid slice range");
1470
+ co_return;
1471
+ }
1472
+ Streams::IBuffer buffer;
1473
+ Streams::IRandomAccessStream stream{ co_await srcFile.OpenAsync(FileAccessMode::Read) };
1474
+ stream.Seek(start);
1475
+ stream.ReadAsync(buffer, length, Streams::InputStreamOptions::None);
1476
+ co_await FileIO::WriteBufferAsync(destFile, buffer);
1477
+
1478
+ promise.Resolve(dest);
1479
+ }
1480
+ catch (...)
1481
+ {
1482
+ promise.Reject("Unable to slice file");
1483
+ }
953
1484
  }
954
1485
 
1486
+ IAsyncAction setTimeout(std::chrono::seconds time) {
1487
+ co_await time;
1488
+ }
955
1489
 
956
- // stat
957
- winrt::fire_and_forget ReactNativeBlobUtil::stat(
958
- std::string path,
959
- std::function<void(std::string, winrt::Microsoft::ReactNative::JSValueObject&)> callback) noexcept
960
- try
1490
+ void ReactNativeBlobUtil::presentOptionsMenu(
1491
+ std::string uri,
1492
+ std::string scheme,
1493
+ ::React::ReactPromise<::React::JSValueArray>&& result) noexcept
961
1494
  {
962
- std::filesystem::path givenPath(path);
963
- givenPath.make_preferred();
964
- bool isDirectory{ std::filesystem::is_directory(path) };
965
-
966
- //std::string resultPath{ winrt::to_string(givenPath.c_str()) };
967
- auto resultPath{ winrt::to_hstring(givenPath.c_str()) };
968
-
969
- // Try to open as folder
970
- IStorageItem item;
971
- if (isDirectory) {
972
- item = co_await StorageFolder::GetFolderFromPathAsync(resultPath);
973
- }
974
- else {
975
- item = co_await StorageFile::GetFileFromPathAsync(resultPath);
976
- }
977
- auto properties{ co_await item.GetBasicPropertiesAsync() };
978
- winrt::Microsoft::ReactNative::JSValueObject fileInfo;
979
- fileInfo["size"] = properties.Size();
980
- fileInfo["filename"] = givenPath.filename().string();
981
- fileInfo["path"] = givenPath.string();
982
- fileInfo["lastModified"] = winrt::clock::to_time_t(properties.DateModified());;
983
- fileInfo["type"] = isDirectory ? "directory" : "file";
984
-
985
- callback("", fileInfo);
1495
+ result.Resolve(::React::JSValueArray{});
986
1496
  }
987
- catch (const hresult_error& ex)
1497
+
1498
+ void ReactNativeBlobUtil::presentOpenInMenu(
1499
+ std::string uri,
1500
+ std::string scheme,
1501
+ ::React::ReactPromise<::React::JSValueArray>&& result) noexcept
988
1502
  {
989
- winrt::Microsoft::ReactNative::JSValueObject emptyObject;
990
- callback(winrt::to_string(ex.message()).c_str(), emptyObject);
1503
+ result.Resolve(::React::JSValueArray{});
991
1504
  }
992
1505
 
993
-
994
- // df
995
- winrt::fire_and_forget ReactNativeBlobUtil::df(
996
- std::function<void(std::string, winrt::Microsoft::ReactNative::JSValueObject&)> callback) noexcept
997
- try
1506
+ void ReactNativeBlobUtil::presentPreview(
1507
+ std::string uri,
1508
+ std::string scheme,
1509
+ ::React::ReactPromise<::React::JSValueArray>&& result) noexcept
998
1510
  {
999
- auto localFolder{ Windows::Storage::ApplicationData::Current().LocalFolder() };
1000
- auto properties{ co_await localFolder.Properties().RetrievePropertiesAsync({L"System.FreeSpace", L"System.Capacity"}) };
1001
-
1002
- winrt::Microsoft::ReactNative::JSValueObject result;
1003
- result["free"] = unbox_value<uint64_t>(properties.Lookup(L"System.FreeSpace"));
1004
- result["total"] = unbox_value<uint64_t>(properties.Lookup(L"System.Capacity"));
1005
- callback("", result);
1511
+ result.Resolve(::React::JSValueArray{});
1006
1512
  }
1007
- catch (...)
1513
+
1514
+ void ReactNativeBlobUtil::excludeFromBackupKey(
1515
+ std::string url,
1516
+ ::React::ReactPromise<::React::JSValueArray>&& result) noexcept
1008
1517
  {
1009
- winrt::Microsoft::ReactNative::JSValueObject emptyObject;
1010
- callback("Failed to get storage usage.", emptyObject);
1518
+ result.Resolve(::React::JSValueArray{});
1011
1519
  }
1012
1520
 
1013
-
1014
- winrt::fire_and_forget ReactNativeBlobUtil::slice(
1015
- std::string src,
1016
- std::string dest,
1017
- uint32_t start,
1018
- uint32_t end,
1019
- winrt::Microsoft::ReactNative::ReactPromise<std::string> promise) noexcept
1020
- try
1521
+ winrt::fire_and_forget ReactNativeBlobUtil::df(
1522
+ std::function<void(::React::JSValueArray)> callback) noexcept
1523
+ {
1524
+ try
1525
+ {
1526
+ auto localFolder = winrt::Windows::Storage::ApplicationData::Current().LocalFolder();
1527
+ auto properties{ co_await localFolder.Properties().RetrievePropertiesAsync({L"System.FreeSpace", L"System.Capacity"}) };
1528
+
1529
+ winrt::Microsoft::ReactNative::JSValueObject result;
1530
+ result["free"] = winrt::unbox_value<uint64_t>(properties.Lookup(L"System.FreeSpace"));
1531
+ result["total"] = winrt::unbox_value<uint64_t>(properties.Lookup(L"System.Capacity"));
1532
+
1533
+ ::React::JSValueArray arr;
1534
+ arr.push_back(::React::JSValueObject(std::move(result)));
1535
+ callback(std::move(arr));
1536
+ }
1537
+ catch (...)
1538
+ {
1539
+ ::React::JSValueArray arr;
1540
+ winrt::Microsoft::ReactNative::JSValueObject error;
1541
+ error["error"] = "Failed to get storage usage.";
1542
+ arr.push_back(::React::JSValueObject(std::move(error)));
1543
+ callback(std::move(arr));
1544
+ }
1545
+
1546
+ }
1547
+
1548
+ void ReactNativeBlobUtil::emitExpiredEvent(
1549
+ std::function<void(std::string)> const& callback) noexcept
1021
1550
  {
1022
- winrt::hstring srcDirectoryPath, srcFileName, destDirectoryPath, destFileName;
1023
- splitPath(src, srcDirectoryPath, srcFileName);
1024
- splitPath(src, destDirectoryPath, destFileName);
1025
-
1026
- StorageFolder srcFolder{ co_await StorageFolder::GetFolderFromPathAsync(srcDirectoryPath) };
1027
- StorageFolder destFolder{ co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath) };
1028
-
1029
- StorageFile srcFile{ co_await srcFolder.GetFileAsync(srcFileName) };
1030
- StorageFile destFile{ co_await destFolder.CreateFileAsync(destFileName, CreationCollisionOption::OpenIfExists) };
1031
-
1032
- uint32_t length{ end - start };
1033
- Streams::IBuffer buffer;
1034
- Streams::IRandomAccessStream stream{ co_await srcFile.OpenAsync(FileAccessMode::Read) };
1035
- stream.Seek(start);
1036
- stream.ReadAsync(buffer, length, Streams::InputStreamOptions::None);
1037
- co_await FileIO::WriteBufferAsync(destFile, buffer);
1038
-
1039
- promise.Resolve(dest);
1551
+ callback("");
1040
1552
  }
1041
- catch (...)
1553
+
1554
+ void ReactNativeBlobUtil::actionViewIntent(
1555
+ std::string path,
1556
+ std::string mime,
1557
+ std::string chooserTitle,
1558
+ ::React::ReactPromise<void>&& result) noexcept
1042
1559
  {
1043
- promise.Reject("Unable to slice file");
1560
+ // No-op: Android API
1561
+ result.Resolve();
1044
1562
  }
1045
1563
 
1564
+ void ReactNativeBlobUtil::addCompleteDownload(
1565
+ ::React::JSValue&& config,
1566
+ ::React::ReactPromise<void>&& result) noexcept
1567
+ {
1568
+ // No-op: Android API
1569
+ result.Resolve();
1570
+ }
1046
1571
 
1047
- IAsyncAction setTimeout(std::chrono::seconds time) {
1048
- co_await time;
1572
+ void ReactNativeBlobUtil::copyToInternal(
1573
+ std::string contentUri,
1574
+ std::string destpath,
1575
+ ::React::ReactPromise<std::string>&& result) noexcept
1576
+ {
1577
+ // No-op: Android API
1578
+ result.Resolve("");
1049
1579
  }
1050
1580
 
1051
- winrt::fire_and_forget ReactNativeBlobUtil::fetchBlob(
1052
- winrt::Microsoft::ReactNative::JSValueObject options,
1053
- std::string taskId,
1054
- std::string method,
1055
- std::wstring url,
1056
- winrt::Microsoft::ReactNative::JSValueObject headers,
1057
- std::string body,
1058
- std::function<void(std::string, std::string, std::string)> callback) noexcept
1581
+ void ReactNativeBlobUtil::copyToMediaStore(
1582
+ ::React::JSValue&& filedata,
1583
+ std::string mt,
1584
+ std::string path,
1585
+ ::React::ReactPromise<std::string>&& result) noexcept
1059
1586
  {
1060
- winrt::Windows::Web::Http::Filters::HttpBaseProtocolFilter filter;
1061
- ReactNativeBlobUtilConfig config{ options };
1062
- filter.AllowAutoRedirect(false);
1063
- if (config.trusty)
1064
- {
1065
- filter.IgnorableServerCertificateErrors().Append(Cryptography::Certificates::ChainValidationResult::Untrusted);
1066
- }
1067
- ReactNativeBlobUtilState eventState;
1068
-
1069
- winrt::Windows::Web::Http::HttpClient httpClient{ filter };
1070
-
1071
- winrt::Windows::Web::Http::HttpMethod httpMethod{ winrt::Windows::Web::Http::HttpMethod::Post() };
1072
- // Delete, Patch, Post, Put, Get, Options, Head
1073
- if (method.compare("DELETE") == 0 || method.compare("delete") == 0)
1074
- {
1075
- httpMethod = winrt::Windows::Web::Http::HttpMethod::Delete();
1076
- }
1077
- else if (method.compare("PUT") == 0 || method.compare("put") == 0)
1078
- {
1079
- httpMethod = winrt::Windows::Web::Http::HttpMethod::Put();
1080
- }
1081
- else if (method.compare("GET") == 0 || method.compare("get") == 0)
1082
- {
1083
- httpMethod = winrt::Windows::Web::Http::HttpMethod::Get();
1084
- }
1085
- else
1086
- {
1087
- callback("Method not supported", "error", "");
1088
- co_return;
1089
- }
1090
-
1091
- winrt::Windows::Web::Http::HttpRequestMessage requestMessage{ httpMethod, Uri{url} };
1092
- bool pathToFile{ body.rfind(prefix, 0) == 0 };
1093
- if (pathToFile)
1094
- {
1095
- std::string contentPath{ body.substr(prefix.length()) };
1096
- size_t fileLength = contentPath.length();
1097
- bool hasTrailingSlash{ contentPath[fileLength - 1] == '\\' || contentPath[fileLength - 1] == '/' };
1098
- winrt::hstring directoryPath, fileName;
1099
- splitPath(hasTrailingSlash ? contentPath.substr(0, fileLength - 1) : contentPath, directoryPath, fileName);
1100
- StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
1101
- StorageFile storageFile{ co_await folder.CreateFileAsync(fileName, CreationCollisionOption::OpenIfExists) };
1102
- IBuffer requestBuffer{ co_await FileIO::ReadBufferAsync(storageFile) };
1103
-
1104
- winrt::Windows::Web::Http::HttpBufferContent requestContent{ requestBuffer };
1105
-
1106
- for (auto const& entry : headers)
1107
- {
1108
- if (!requestMessage.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString())))
1109
- {
1110
- requestContent.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString()));
1111
- }
1112
- }
1113
- requestMessage.Content(requestContent);
1114
- }
1115
- else if (!body.empty()) {
1116
- winrt::Windows::Web::Http::HttpStringContent requestString{ winrt::to_hstring(body) };
1117
-
1118
- for (auto const& entry : headers)
1119
- {
1120
- if (!requestMessage.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString())))
1121
- {
1122
- requestString.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString()));
1123
- }
1124
- }
1125
- requestMessage.Content(requestString);
1126
- }
1127
- else {
1128
- for (auto const& entry : headers)
1129
- {
1130
- requestMessage.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString()));
1131
- }
1132
- }
1133
-
1134
- auto exists{ uploadProgressMap.find(taskId) };
1135
- if (exists != uploadProgressMap.end()) {
1136
- auto progress{ uploadProgressMap[taskId] };
1137
- uint64_t bodySize{ (co_await requestMessage.Content().ReadAsBufferAsync()).Length() };
1138
- auto contentStream{ co_await requestMessage.Content().ReadAsInputStreamAsync() };
1139
- Buffer buffer{ 10 * 1024 };
1140
- uint64_t read{ 0 };
1141
-
1142
- if (progress.count > -1) {
1143
- uint64_t progressInterval{ bodySize / 100 * progress.count };
1144
- for (;;) {
1145
-
1146
- buffer.Length(0);
1147
- auto readBuffer = co_await contentStream.ReadAsync(buffer, buffer.Capacity(), InputStreamOptions::None);
1148
- read += readBuffer.Length();
1149
-
1150
- if (readBuffer.Length() == 0)
1151
- {
1152
- break;
1153
- }
1154
-
1155
- if (read >= progressInterval) {
1156
- m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", L"ReactNativeBlobUtilProgress-upload",
1157
- Microsoft::ReactNative::JSValueObject{
1158
- { "taskId", taskId },
1159
- { "written", read },
1160
- { "total", bodySize },
1161
- });
1162
- read = 0;
1163
- }
1164
- }
1165
- }
1166
- else if (progress.interval > -1) {
1167
- int64_t initialProgressTime{ winrt::clock::now().time_since_epoch().count() / 10000 };
1168
- int64_t currentProgressTime;
1169
- for (;;) {
1170
- buffer.Length(0);
1171
-
1172
- auto readBuffer = co_await contentStream.ReadAsync(buffer, buffer.Capacity(), InputStreamOptions::None);
1173
- read += readBuffer.Length();
1174
-
1175
- if (readBuffer.Length() == 0)
1176
- {
1177
- break;
1178
- }
1179
-
1180
- currentProgressTime = winrt::clock::now().time_since_epoch().count() / 10000;
1181
- if (currentProgressTime - initialProgressTime >= progress.interval) {
1182
- m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", L"ReactNativeBlobUtilProgress-upload",
1183
- Microsoft::ReactNative::JSValueObject{
1184
- { "taskId", taskId },
1185
- { "written", read },
1186
- { "total", bodySize },
1187
- });
1188
- initialProgressTime = winrt::clock::now().time_since_epoch().count() / 10000;
1189
- }
1190
- }
1191
- }
1192
- }
1193
-
1194
- std::string error;
1195
- auto cancellationTimer{ setTimeout(config.timeout) };
1196
- cancellationTimer.Completed([weak_this = weak_from_this(), taskId, error](IAsyncAction const& action, AsyncStatus status) {
1197
- if (status == AsyncStatus::Completed) {
1198
- auto strong_this{ weak_this.lock() };
1199
- if (strong_this) {
1200
- strong_this->m_tasks.Cancel(taskId);
1201
- {
1202
- std::scoped_lock lock{ strong_this->m_mutex };
1203
- strong_this->uploadProgressMap.extract(taskId);
1204
- strong_this->downloadProgressMap.extract(taskId);
1205
- }
1206
- }
1207
- }
1208
- });
1209
- try {
1210
- co_await m_tasks.Add(taskId, ProcessRequestAsync(taskId, filter, requestMessage, config, callback, error));
1211
- }
1212
- catch (...) {
1213
-
1214
- }
1215
- if (!error.empty()) {
1216
- if (cancellationTimer.Status() != AsyncStatus::Completed) {
1217
- callback(error, "error", "");
1218
- }
1219
- else {
1220
- callback("React-native-blobl-util request timed out", "error", "");
1221
- }
1222
- }
1223
-
1224
- cancellationTimer.Cancel();
1225
- m_tasks.Cancel(taskId);
1226
- {
1227
- std::scoped_lock lock{ m_mutex };
1228
- uploadProgressMap.extract(taskId);
1229
- downloadProgressMap.extract(taskId);
1230
- }
1587
+ // No-op: Android API
1588
+ result.Resolve("");
1231
1589
  }
1232
1590
 
1233
- winrt::fire_and_forget ReactNativeBlobUtil::fetchBlobForm(
1234
- winrt::Microsoft::ReactNative::JSValueObject options,
1235
- std::string taskId,
1236
- std::string method,
1237
- std::wstring url,
1238
- winrt::Microsoft::ReactNative::JSValueObject headers,
1239
- winrt::Microsoft::ReactNative::JSValueArray body,
1240
- std::function<void(std::string, std::string, std::string)> callback) noexcept
1591
+ void ReactNativeBlobUtil::createMediaFile(
1592
+ ::React::JSValue&& filedata,
1593
+ std::string mt,
1594
+ ::React::ReactPromise<std::string>&& result) noexcept
1241
1595
  {
1242
- winrt::hstring boundary{ L"-----" };
1243
- winrt::Windows::Web::Http::Filters::HttpBaseProtocolFilter filter;
1244
-
1245
- ReactNativeBlobUtilConfig config{ options };
1246
-
1247
- filter.AllowAutoRedirect(false);
1248
-
1249
- if (config.trusty)
1250
- {
1251
- filter.IgnorableServerCertificateErrors().Append(Cryptography::Certificates::ChainValidationResult::Untrusted);
1252
- }
1253
-
1254
- winrt::Windows::Web::Http::HttpClient httpClient{ filter };
1255
-
1256
- winrt::Windows::Web::Http::HttpMethod httpMethod{ winrt::Windows::Web::Http::HttpMethod::Post() };
1257
- // Delete, Patch, Post, Put, Get, Options, Head
1258
- if (method.compare("DELETE") == 0 || method.compare("delete") == 0)
1259
- {
1260
- httpMethod = winrt::Windows::Web::Http::HttpMethod::Delete();
1261
- }
1262
- else if (method.compare("PUT") == 0 || method.compare("put") == 0)
1263
- {
1264
- httpMethod = winrt::Windows::Web::Http::HttpMethod::Put();
1265
- }
1266
- else if (method.compare("GET") == 0 || method.compare("get") == 0)
1267
- {
1268
- httpMethod = winrt::Windows::Web::Http::HttpMethod::Get();
1269
- }
1270
- else if (method.compare("POST") != 0 && method.compare("post") != 0)
1271
- {
1272
- callback("Method not supported", "error", "");
1273
- co_return;
1274
- }
1275
-
1276
- winrt::Windows::Web::Http::HttpRequestMessage requestMessage{ httpMethod, Uri{url} };
1277
- winrt::Windows::Web::Http::HttpMultipartFormDataContent requestContent{ boundary };
1278
-
1279
- for (auto const& entry : headers)
1280
- {
1281
- if (!requestMessage.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString())))
1282
- {
1283
- requestContent.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString()));
1284
- }
1285
- }
1286
-
1287
- for (auto& entry : body) {
1288
- auto& items{ entry.AsObject() };
1289
-
1290
- auto data{ items["data"].AsString() };
1291
- bool pathToFile{ data.rfind(prefix, 0) == 0 };
1292
- if (pathToFile)
1293
- {
1294
- std::string contentPath{ data.substr(prefix.length()) };
1295
- size_t fileLength = contentPath.length();
1296
- bool hasTrailingSlash{ contentPath[fileLength - 1] == '\\' || contentPath[fileLength - 1] == '/' };
1297
- winrt::hstring directoryPath, fileName;
1298
- splitPath(hasTrailingSlash ? contentPath.substr(0, fileLength - 1) : contentPath, directoryPath, fileName);
1299
- StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
1300
- StorageFile storageFile = co_await folder.CreateFileAsync(fileName, CreationCollisionOption::OpenIfExists);
1301
- IBuffer requestBuffer{ co_await FileIO::ReadBufferAsync(storageFile) };
1302
-
1303
- winrt::Windows::Web::Http::HttpBufferContent requestBufferContent{ requestBuffer };
1304
-
1305
- if (!items["type"].IsNull()) {
1306
- requestBufferContent.Headers().TryAppendWithoutValidation(L"content-type", winrt::to_hstring(items["type"].AsString()));
1307
- }
1308
-
1309
- auto name{ items["name"].IsNull() ? L"" : winrt::to_hstring(items["name"].AsString()) };
1310
- if (name.size() <= 0) {
1311
- requestContent.Add(requestBufferContent);
1312
- continue;
1313
- }
1314
- auto filename{ items["filename"].IsNull() ? L"" : winrt::to_hstring(items["filename"].AsString()) };
1315
- if (filename.size() <= 0) {
1316
- requestContent.Add(requestBufferContent, name);
1317
- }
1318
- else {
1319
- requestContent.Add(requestBufferContent, name, filename);
1320
- }
1321
- }
1322
- else {
1323
- winrt::Windows::Web::Http::HttpStringContent dataContents{ winrt::to_hstring(data) };
1324
- if (!items["type"].IsNull()) {
1325
- dataContents.Headers().TryAppendWithoutValidation(L"content-type", winrt::to_hstring(items["type"].AsString()));
1326
- }
1327
-
1328
- auto name{ items["name"].IsNull() ? L"" : winrt::to_hstring(items["name"].AsString()) };
1329
- if (name.size() <= 0) {
1330
- requestContent.Add(dataContents);
1331
- continue;
1332
- }
1333
- auto filename{ items["filename"].IsNull() ? L"" : winrt::to_hstring(items["filename"].AsString()) };
1334
- if (filename.size() <= 0) {
1335
- requestContent.Add(dataContents, name);
1336
- }
1337
- else {
1338
- requestContent.Add(dataContents, name, filename);
1339
- }
1340
- }
1341
- }
1342
- requestMessage.Content(requestContent);
1343
-
1344
- auto exists{ uploadProgressMap.find(taskId) };
1345
- if (exists != uploadProgressMap.end()) {
1346
- auto progress{ uploadProgressMap[taskId] };
1347
- uint64_t bodySize{ (co_await requestMessage.Content().ReadAsBufferAsync()).Length() };
1348
- auto contentStream{ co_await requestMessage.Content().ReadAsInputStreamAsync() };
1349
- Buffer buffer{ 10 * 1024 };
1350
- uint64_t read{ 0 };
1351
-
1352
- if (progress.count > -1) {
1353
- uint64_t progressInterval{ bodySize / 100 * progress.count };
1354
- for (;;) {
1355
-
1356
- buffer.Length(0);
1357
- auto readBuffer = co_await contentStream.ReadAsync(buffer, buffer.Capacity(), InputStreamOptions::None);
1358
- read += readBuffer.Length();
1359
-
1360
- if (readBuffer.Length() == 0)
1361
- {
1362
- break;
1363
- }
1364
-
1365
- if (read >= progressInterval) {
1366
- m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", L"ReactNativeBlobUtilProgress-upload",
1367
- Microsoft::ReactNative::JSValueObject{
1368
- { "taskId", taskId },
1369
- { "written", read },
1370
- { "total", bodySize },
1371
- });
1372
- read = 0;
1373
- }
1374
- }
1375
- }
1376
- else if (progress.interval > -1) {
1377
- int64_t initialProgressTime{ winrt::clock::now().time_since_epoch().count() / 10000 };
1378
- int64_t currentProgressTime;
1379
- for (;;) {
1380
- buffer.Length(0);
1381
-
1382
- auto readBuffer = co_await contentStream.ReadAsync(buffer, buffer.Capacity(), InputStreamOptions::None);
1383
- read += readBuffer.Length();
1384
-
1385
- if (readBuffer.Length() == 0)
1386
- {
1387
- break;
1388
- }
1389
-
1390
- currentProgressTime = winrt::clock::now().time_since_epoch().count() / 10000;
1391
- if (currentProgressTime - initialProgressTime >= progress.interval) {
1392
- m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", L"ReactNativeBlobUtilProgress-upload",
1393
- Microsoft::ReactNative::JSValueObject{
1394
- { "taskId", taskId },
1395
- { "written", read },
1396
- { "total", bodySize },
1397
- });
1398
- initialProgressTime = winrt::clock::now().time_since_epoch().count() / 10000;
1399
- }
1400
- }
1401
- }
1402
- }
1403
-
1404
- std::string error;
1405
- auto cancellationTimer{ setTimeout(config.timeout) };
1406
- cancellationTimer.Completed([weak_this = weak_from_this(), taskId, error](IAsyncAction const& action, AsyncStatus status) {
1407
- if (status == AsyncStatus::Completed) {
1408
- auto strong_this{ weak_this.lock() };
1409
- if (strong_this) {
1410
- strong_this->m_tasks.Cancel(taskId);
1411
- {
1412
- std::scoped_lock lock{ strong_this->m_mutex };
1413
- strong_this->uploadProgressMap.extract(taskId);
1414
- strong_this->downloadProgressMap.extract(taskId);
1415
- }
1416
- }
1417
- }
1418
- });
1419
- try {
1420
- co_await m_tasks.Add(taskId, ProcessRequestAsync(taskId, filter, requestMessage, config, callback, error));
1421
- }
1422
- catch (...) {
1423
-
1424
- }
1425
- if (!error.empty()) {
1426
- if (cancellationTimer.Status() != AsyncStatus::Completed) {
1427
- callback(error, "error", "");
1428
- }
1429
- else {
1430
- callback("ReactNativeBlobUtil request timed out", "error", "");
1431
- }
1432
- }
1433
-
1434
- cancellationTimer.Cancel();
1435
- m_tasks.Cancel(taskId);
1436
- {
1437
- std::scoped_lock lock{ m_mutex };
1438
- uploadProgressMap.extract(taskId);
1439
- downloadProgressMap.extract(taskId);
1440
- }
1596
+ // No-op: Android API
1597
+ result.Resolve("");
1441
1598
  }
1442
1599
 
1443
- void ReactNativeBlobUtil::enableProgressReport(
1444
- std::string taskId,
1445
- int interval,
1446
- int count) noexcept {
1447
- ReactNativeBlobUtilProgressConfig config{ count, interval };
1448
- std::scoped_lock lock{ m_mutex };
1449
- downloadProgressMap.try_emplace(taskId, config);
1600
+ void ReactNativeBlobUtil::getBlob(
1601
+ std::string contentUri,
1602
+ std::string encoding,
1603
+ ::React::ReactPromise<::React::JSValueArray>&& result) noexcept
1604
+ {
1605
+ // No-op: Android API
1606
+ result.Resolve(::React::JSValueArray{});
1450
1607
  }
1451
1608
 
1452
- // enableUploadProgressReport
1453
- void ReactNativeBlobUtil::enableUploadProgressReport(
1454
- std::string taskId,
1455
- int interval,
1456
- int count) noexcept {
1457
- ReactNativeBlobUtilProgressConfig config{ count, interval };
1458
- std::scoped_lock lock{ m_mutex };
1459
- uploadProgressMap.try_emplace(taskId, config);
1609
+ void ReactNativeBlobUtil::getContentIntent(
1610
+ std::string mime,
1611
+ ::React::ReactPromise<std::string>&& result) noexcept
1612
+ {
1613
+ // No-op: Android API
1614
+ result.Resolve("");
1460
1615
  }
1461
1616
 
1462
- // cancelRequest
1463
- void ReactNativeBlobUtil::cancelRequest(
1464
- std::string taskId,
1465
- std::function<void(std::string, std::string)> callback) noexcept
1466
- try
1617
+ void ReactNativeBlobUtil::getSDCardDir(
1618
+ ::React::ReactPromise<std::string>&& result) noexcept
1467
1619
  {
1468
- m_tasks.Cancel(taskId);
1469
- callback("", taskId);
1620
+ // No-op: Android API
1621
+ result.Resolve("");
1470
1622
  }
1471
- catch (const hresult_error& ex)
1623
+
1624
+ void ReactNativeBlobUtil::getSDCardApplicationDir(
1625
+ ::React::ReactPromise<std::string>&& result) noexcept
1472
1626
  {
1473
- callback(winrt::to_string(ex.message()), "");
1627
+ // No-op: Android API
1628
+ result.Resolve("");
1474
1629
  }
1475
1630
 
1476
- winrt::fire_and_forget ReactNativeBlobUtil::removeSession(
1477
- winrt::Microsoft::ReactNative::JSValueArray paths,
1478
- std::function<void(std::string)> callback) noexcept
1479
- try
1631
+ void ReactNativeBlobUtil::scanFile(
1632
+ ::React::JSValueArray&& pairs,
1633
+ std::function<void(::React::JSValueArray const&)> const& callback) noexcept
1480
1634
  {
1481
- for (auto& path : paths)
1482
- {
1483
- std::filesystem::path toDelete{ path.AsString() };
1484
- toDelete.make_preferred();
1485
- StorageFile file{ co_await StorageFile::GetFileFromPathAsync(winrt::to_hstring(toDelete.c_str())) };
1486
- co_await file.DeleteAsync();
1487
- }
1488
- callback("");
1635
+ // No-op: Android API
1636
+ callback(::React::JSValueArray{});
1489
1637
  }
1490
- catch (const hresult_error& ex)
1638
+
1639
+ void ReactNativeBlobUtil::writeToMediaFile(
1640
+ std::string fileUri,
1641
+ std::string path,
1642
+ bool transformFile,
1643
+ ::React::ReactPromise<std::string>&& result) noexcept
1491
1644
  {
1492
- callback(winrt::to_string(ex.message()).c_str());
1645
+ // No-op: Android API
1646
+ result.Resolve("");
1493
1647
  }
1494
1648
 
1495
- void ReactNativeBlobUtil::closeStream(
1496
- std::string streamId,
1497
- std::function<void(std::string)> callback) noexcept
1498
- try
1649
+ void ReactNativeBlobUtil::addListener(
1650
+ std::string eventName) noexcept
1499
1651
  {
1500
- auto stream{ m_streamMap.find(streamId)->second };
1501
- stream.streamInstance.Close();
1502
- m_streamMap.extract(streamId);
1503
- callback("");
1652
+ // No-op
1504
1653
  }
1505
- catch (const hresult_error& ex)
1654
+
1655
+ void ReactNativeBlobUtil::removeListeners(
1656
+ double count) noexcept
1506
1657
  {
1507
- callback(winrt::to_string(ex.message()).c_str());
1658
+ // No-op
1508
1659
  }
1509
1660
 
1510
-
1511
1661
  void ReactNativeBlobUtil::splitPath(const std::string& fullPath, winrt::hstring& directoryPath, winrt::hstring& fileName) noexcept
1512
1662
  {
1513
1663
  std::filesystem::path path{ fullPath };
@@ -1526,14 +1676,6 @@ void ReactNativeBlobUtil::splitPath(const std::wstring& fullPath, winrt::hstring
1526
1676
  fileName = path.has_filename() ? winrt::to_hstring(path.filename().c_str()) : L"";
1527
1677
  }
1528
1678
 
1529
- void ReactNativeBlobUtil::addListener(std::string eventName) noexcept
1530
- {
1531
- }
1532
-
1533
- void ReactNativeBlobUtil::removeListeners(double count) noexcept
1534
- {
1535
- }
1536
-
1537
1679
  winrt::Windows::Foundation::IAsyncAction ReactNativeBlobUtil::ProcessRequestAsync(
1538
1680
  const std::string& taskId,
1539
1681
  const winrt::Windows::Web::Http::Filters::HttpBaseProtocolFilter& filter,
@@ -1692,10 +1834,4 @@ catch (...) {
1692
1834
  co_return;
1693
1835
  }
1694
1836
 
1695
-
1696
- ReactNativeBlobUtilStream::ReactNativeBlobUtilStream(Streams::IRandomAccessStream& _streamInstance, EncodingOptions _encoding) noexcept
1697
- : streamInstance{ std::move(_streamInstance) }
1698
- , encoding{ _encoding }
1699
- {
1700
- }
1701
-
1837
+ } // namespace winrt::ReactNativeBlobUtil