react-native-blob-util 0.19.0 → 0.19.2
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.
- package/.eslintrc.js +56 -56
- package/LICENSE +21 -21
- package/Migration.md +41 -41
- package/README.md +1071 -1071
- package/android/gradle.properties +1 -1
- package/android/proguard-rules.pro +17 -17
- package/android/src/main/AndroidManifest.xml +39 -40
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConfig.java +57 -57
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConst.java +20 -20
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFS.java +2 -2
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFileTransformer.java +9 -9
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilImpl.java +431 -431
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilProgressConfig.java +39 -39
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java +972 -968
- package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilDefaultResp.java +98 -98
- package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilFileResp.java +157 -157
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileDescription.java +19 -19
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileProvider.java +4 -4
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/MimeType.java +70 -70
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/PathResolver.java +223 -223
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/Tls12SocketFactory.java +68 -68
- package/android/src/main/res/values/strings.xml +3 -3
- package/android/src/main/res/xml/provider_paths.xml +12 -12
- package/android/src/newarch/java/com/ReactNativeBlobUtil/ReactNativeBlobUtil.java +1 -1
- package/android.js +58 -58
- package/class/ReactNativeBlobUtilCanceledFetchError.js +9 -9
- package/class/ReactNativeBlobUtilFile.js +5 -5
- package/class/ReactNativeBlobUtilReadStream.js +84 -84
- package/class/ReactNativeBlobUtilSession.js +68 -68
- package/class/ReactNativeBlobUtilWriteStream.js +50 -50
- package/class/StatefulPromise.js +7 -7
- package/codegenSpecs/NativeBlobUtils.js +86 -82
- package/components/Fetch.onPress.js +10 -10
- package/components/Fetch.when.js +15 -15
- package/fetch.js +344 -344
- package/fs.js +480 -480
- package/index.d.ts +871 -871
- package/index.js +64 -64
- package/index.js.flow +191 -191
- package/index.web.js +1 -1
- package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.h +55 -55
- package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.mm +993 -993
- package/ios/ReactNativeBlobUtil.xcodeproj/project.pbxproj +403 -403
- package/ios/ReactNativeBlobUtilConst.h +58 -58
- package/ios/ReactNativeBlobUtilConst.mm +47 -47
- package/ios/ReactNativeBlobUtilFS.h +108 -108
- package/ios/ReactNativeBlobUtilFS.mm +977 -977
- package/ios/ReactNativeBlobUtilFileTransformer.h +23 -23
- package/ios/ReactNativeBlobUtilFileTransformer.mm +20 -20
- package/ios/ReactNativeBlobUtilNetwork.h +49 -49
- package/ios/ReactNativeBlobUtilNetwork.mm +159 -159
- package/ios/ReactNativeBlobUtilProgress.h +32 -32
- package/ios/ReactNativeBlobUtilProgress.mm +57 -57
- package/ios/ReactNativeBlobUtilReqBuilder.h +37 -37
- package/ios/ReactNativeBlobUtilReqBuilder.mm +292 -292
- package/ios/ReactNativeBlobUtilRequest.h +48 -48
- package/ios/ReactNativeBlobUtilRequest.mm +535 -535
- package/ios.js +61 -61
- package/json-stream.js +42 -42
- package/lib/oboe-browser.js +2703 -2703
- package/mediacollection.js +36 -36
- package/package.json +55 -55
- package/polyfill/Blob.js +362 -362
- package/polyfill/Event.js +11 -11
- package/polyfill/EventTarget.js +78 -78
- package/polyfill/Fetch.js +219 -219
- package/polyfill/File.js +27 -27
- package/polyfill/FileReader.js +91 -91
- package/polyfill/ProgressEvent.js +32 -32
- package/polyfill/XMLHttpRequest.js +466 -466
- package/polyfill/XMLHttpRequestEventTarget.js +126 -126
- package/polyfill/index.js +11 -11
- package/react-native-blob-util.podspec +45 -45
- package/types.js +69 -69
- package/utils/log.js +40 -40
- package/utils/unicode.js +7 -7
- package/utils/uri.js +33 -33
- package/utils/uuid.js +4 -4
- package/windows/README.md +2 -2
- package/windows/ReactNativeBlobUtil/PropertySheet.props +15 -15
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.cpp +1701 -1693
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.def +3 -3
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.h +379 -366
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.vcxproj +181 -181
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.vcxproj.filters +31 -31
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.cpp +15 -15
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.h +20 -20
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.idl +9 -9
- package/windows/ReactNativeBlobUtil/packages.config +3 -3
- package/windows/ReactNativeBlobUtil/pch.cpp +1 -1
- package/windows/ReactNativeBlobUtil/pch.h +3 -3
|
@@ -1,1693 +1,1701 @@
|
|
|
1
|
-
#include "pch.h"
|
|
2
|
-
#include "ReactNativeBlobUtil.h"
|
|
3
|
-
#include <winrt/Windows.ApplicationModel.Activation.h>
|
|
4
|
-
#include <winrt/Windows.Security.Cryptography.h>
|
|
5
|
-
#include <winrt/Windows.Security.Cryptography.Core.h>
|
|
6
|
-
#include <winrt/Windows.Storage.FileProperties.h>
|
|
7
|
-
#include <winrt/Windows.Storage.Streams.h>
|
|
8
|
-
#include <winrt/Windows.Storage.h>
|
|
9
|
-
#include <winrt/Windows.Web.Http.h>
|
|
10
|
-
#include <winrt/Windows.Web.Http.Headers.h>
|
|
11
|
-
#include <winrt/windows.web.http.filters.h>
|
|
12
|
-
#include <filesystem>
|
|
13
|
-
#include <sstream>
|
|
14
|
-
|
|
15
|
-
using namespace winrt;
|
|
16
|
-
using namespace winrt::Windows::ApplicationModel;
|
|
17
|
-
using namespace winrt::Windows::Storage;
|
|
18
|
-
using namespace winrt::Windows::Storage::Streams;
|
|
19
|
-
using namespace winrt::Windows::Foundation;
|
|
20
|
-
using namespace winrt::Windows::Security::Cryptography;
|
|
21
|
-
using namespace winrt::Windows::Security::Cryptography::Core;
|
|
22
|
-
using namespace std::chrono_literals;
|
|
23
|
-
|
|
24
|
-
CancellationDisposable::CancellationDisposable(IAsyncInfo const& async, std::function<void()>&& onCancel) noexcept
|
|
25
|
-
: m_async{ async }
|
|
26
|
-
, m_onCancel{ std::move(onCancel) }
|
|
27
|
-
{
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
CancellationDisposable::CancellationDisposable(CancellationDisposable&& other) noexcept
|
|
31
|
-
: m_async{ std::move(other.m_async) }
|
|
32
|
-
, m_onCancel{ std::move(other.m_onCancel) }
|
|
33
|
-
{
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
CancellationDisposable& CancellationDisposable::operator=(CancellationDisposable&& other) noexcept
|
|
37
|
-
{
|
|
38
|
-
if (this != &other)
|
|
39
|
-
{
|
|
40
|
-
CancellationDisposable temp{ std::move(*this) };
|
|
41
|
-
m_async = std::move(other.m_async);
|
|
42
|
-
m_onCancel = std::move(other.m_onCancel);
|
|
43
|
-
}
|
|
44
|
-
return *this;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
CancellationDisposable::~CancellationDisposable() noexcept
|
|
48
|
-
{
|
|
49
|
-
Cancel();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
void CancellationDisposable::Cancel() noexcept
|
|
53
|
-
{
|
|
54
|
-
if (m_async)
|
|
55
|
-
{
|
|
56
|
-
if (m_async.Status() == AsyncStatus::Started)
|
|
57
|
-
{
|
|
58
|
-
m_async.Cancel();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (m_onCancel)
|
|
62
|
-
{
|
|
63
|
-
m_onCancel();
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
TaskCancellationManager::~TaskCancellationManager() noexcept
|
|
69
|
-
{
|
|
70
|
-
// Do the explicit cleaning to make sure that CancellationDisposable
|
|
71
|
-
// destructors run while this instance still has valid fields because
|
|
72
|
-
// they are used by the onCancel callback.
|
|
73
|
-
// We also want to clear the m_pendingTasks before running the
|
|
74
|
-
// CancellationDisposable destructors since they touch the m_pendingTasks.
|
|
75
|
-
std::map<TaskId, CancellationDisposable> pendingTasks;
|
|
76
|
-
{
|
|
77
|
-
std::scoped_lock lock{ m_mutex };
|
|
78
|
-
pendingTasks = std::move(m_pendingTasks);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
IAsyncAction TaskCancellationManager::Add(TaskId taskId, IAsyncAction const& asyncAction) noexcept
|
|
84
|
-
{
|
|
85
|
-
std::scoped_lock lock{ m_mutex };
|
|
86
|
-
m_pendingTasks.try_emplace(taskId, asyncAction, [this, taskId]()
|
|
87
|
-
{
|
|
88
|
-
Cancel(taskId);
|
|
89
|
-
});
|
|
90
|
-
return asyncAction;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
void TaskCancellationManager::Cancel(TaskId taskId) noexcept
|
|
94
|
-
{
|
|
95
|
-
// The destructor of the token does the cancellation. We must do it outside of lock.
|
|
96
|
-
CancellationDisposable token;
|
|
97
|
-
|
|
98
|
-
{
|
|
99
|
-
std::scoped_lock lock{ m_mutex };
|
|
100
|
-
if (!m_pendingTasks.empty())
|
|
101
|
-
{
|
|
102
|
-
if (auto it = m_pendingTasks.find(taskId); it != m_pendingTasks.end())
|
|
103
|
-
{
|
|
104
|
-
token = std::move(it->second);
|
|
105
|
-
m_pendingTasks.erase(it);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
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
|
|
147
|
-
{
|
|
148
|
-
m_reactContext = reactContext;
|
|
149
|
-
}
|
|
150
|
-
|
|
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)
|
|
244
|
-
{
|
|
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
|
-
}
|
|
255
|
-
|
|
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
|
|
261
|
-
{
|
|
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
|
-
|
|
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)
|
|
283
|
-
{
|
|
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
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
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)
|
|
354
|
-
{
|
|
355
|
-
promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "EUNSPECIFIED: " + winrt::to_string(ex.message()) + "; " + path });
|
|
356
|
-
}
|
|
357
|
-
|
|
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
|
|
364
|
-
{
|
|
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());
|
|
393
|
-
|
|
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 });
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
// writeStream
|
|
403
|
-
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
|
|
409
|
-
{
|
|
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);
|
|
449
|
-
}
|
|
450
|
-
catch (const hresult_error& ex)
|
|
451
|
-
{
|
|
452
|
-
callback("EUNSPECIFIED", "Failed to create write stream at path '" + path + "'; " + winrt::to_string(ex.message().c_str()), "");
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
// writeChunk
|
|
457
|
-
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)
|
|
482
|
-
{
|
|
483
|
-
callback(winrt::to_string(ex.message().c_str()));
|
|
484
|
-
}
|
|
485
|
-
|
|
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)
|
|
505
|
-
{
|
|
506
|
-
callback(winrt::to_string(ex.message().c_str()));
|
|
507
|
-
}
|
|
508
|
-
|
|
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
|
|
517
|
-
{
|
|
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)
|
|
624
|
-
{
|
|
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
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
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)
|
|
665
|
-
{
|
|
666
|
-
promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "Error creating folder " + path + ", error: " + winrt::to_string(ex.message().c_str()) });
|
|
667
|
-
}
|
|
668
|
-
|
|
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)
|
|
703
|
-
{
|
|
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
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
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
|
|
727
|
-
{
|
|
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
|
-
}
|
|
734
|
-
|
|
735
|
-
winrt::hstring directoryPath, fileName;
|
|
736
|
-
splitPath(path, directoryPath, fileName);
|
|
737
|
-
|
|
738
|
-
StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
|
|
739
|
-
StorageFile file{ co_await folder.GetFileAsync(fileName) };
|
|
740
|
-
|
|
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
|
-
}
|
|
747
|
-
|
|
748
|
-
CryptographyCore::HashAlgorithmProvider provider{ search->second() };
|
|
749
|
-
Streams::IBuffer buffer{ co_await FileIO::ReadBufferAsync(file) };
|
|
750
|
-
|
|
751
|
-
auto hashedBuffer{ provider.HashData(buffer) };
|
|
752
|
-
std::wstring result{ Cryptography::CryptographicBuffer::EncodeToHexString(hashedBuffer) };
|
|
753
|
-
|
|
754
|
-
promise.Resolve(result);
|
|
755
|
-
}
|
|
756
|
-
catch (const hresult_error& ex)
|
|
757
|
-
{
|
|
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
|
-
}
|
|
773
|
-
|
|
774
|
-
|
|
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);
|
|
783
|
-
|
|
784
|
-
StorageFolder targetDirectory{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
|
|
785
|
-
|
|
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
|
-
}
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
// mv
|
|
809
|
-
winrt::fire_and_forget ReactNativeBlobUtil::mv(
|
|
810
|
-
std::string src, // from
|
|
811
|
-
std::string dest, // to
|
|
812
|
-
std::function<void(std::string)> callback) noexcept
|
|
813
|
-
{
|
|
814
|
-
try
|
|
815
|
-
{
|
|
816
|
-
winrt::hstring srcDirectoryPath, srcFileName;
|
|
817
|
-
splitPath(src, srcDirectoryPath, srcFileName);
|
|
818
|
-
|
|
819
|
-
winrt::hstring destDirectoryPath, destFileName;
|
|
820
|
-
splitPath(dest, destDirectoryPath, destFileName);
|
|
821
|
-
|
|
822
|
-
StorageFolder srcFolder{ co_await StorageFolder::GetFolderFromPathAsync(srcDirectoryPath) };
|
|
823
|
-
StorageFolder destFolder{ co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath) };
|
|
824
|
-
StorageFile file{ co_await srcFolder.GetFileAsync(srcFileName) };
|
|
825
|
-
|
|
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
|
-
}
|
|
842
|
-
|
|
843
|
-
|
|
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
|
|
850
|
-
{
|
|
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("");
|
|
864
|
-
}
|
|
865
|
-
catch (const hresult_error& ex)
|
|
866
|
-
{
|
|
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());
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
// exists
|
|
877
|
-
void ReactNativeBlobUtil::exists(
|
|
878
|
-
std::string path,
|
|
879
|
-
std::function<void(bool, bool)> callback) noexcept
|
|
880
|
-
{
|
|
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);
|
|
886
|
-
}
|
|
887
|
-
|
|
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
|
|
894
|
-
{
|
|
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);
|
|
911
|
-
}
|
|
912
|
-
catch (const hresult_error& ex)
|
|
913
|
-
{
|
|
914
|
-
callback(winrt::to_string(ex.message()), false);
|
|
915
|
-
}
|
|
916
|
-
|
|
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
|
|
923
|
-
{
|
|
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);
|
|
947
|
-
}
|
|
948
|
-
catch (...)
|
|
949
|
-
{
|
|
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);
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
|
|
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
|
|
961
|
-
{
|
|
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);
|
|
986
|
-
}
|
|
987
|
-
catch (const hresult_error& ex)
|
|
988
|
-
{
|
|
989
|
-
winrt::Microsoft::ReactNative::JSValueObject emptyObject;
|
|
990
|
-
callback(winrt::to_string(ex.message()).c_str(), emptyObject);
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
// df
|
|
995
|
-
winrt::fire_and_forget ReactNativeBlobUtil::df(
|
|
996
|
-
std::function<void(std::string, winrt::Microsoft::ReactNative::JSValueObject&)> callback) noexcept
|
|
997
|
-
try
|
|
998
|
-
{
|
|
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);
|
|
1006
|
-
}
|
|
1007
|
-
catch (...)
|
|
1008
|
-
{
|
|
1009
|
-
winrt::Microsoft::ReactNative::JSValueObject emptyObject;
|
|
1010
|
-
callback("Failed to get storage usage.", emptyObject);
|
|
1011
|
-
}
|
|
1012
|
-
|
|
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
|
|
1021
|
-
{
|
|
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);
|
|
1040
|
-
}
|
|
1041
|
-
catch (...)
|
|
1042
|
-
{
|
|
1043
|
-
promise.Reject("Unable to slice file");
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
IAsyncAction setTimeout(std::chrono::seconds time) {
|
|
1048
|
-
co_await time;
|
|
1049
|
-
}
|
|
1050
|
-
|
|
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
|
|
1059
|
-
{
|
|
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
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
|
|
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
|
|
1241
|
-
{
|
|
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
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
|
|
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);
|
|
1450
|
-
}
|
|
1451
|
-
|
|
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);
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
// cancelRequest
|
|
1463
|
-
void ReactNativeBlobUtil::cancelRequest(
|
|
1464
|
-
std::string taskId,
|
|
1465
|
-
std::function<void(std::string, std::string)> callback) noexcept
|
|
1466
|
-
try
|
|
1467
|
-
{
|
|
1468
|
-
m_tasks.Cancel(taskId);
|
|
1469
|
-
callback("", taskId);
|
|
1470
|
-
}
|
|
1471
|
-
catch (const hresult_error& ex)
|
|
1472
|
-
{
|
|
1473
|
-
callback(winrt::to_string(ex.message()), "");
|
|
1474
|
-
}
|
|
1475
|
-
|
|
1476
|
-
winrt::fire_and_forget ReactNativeBlobUtil::removeSession(
|
|
1477
|
-
winrt::Microsoft::ReactNative::JSValueArray paths,
|
|
1478
|
-
std::function<void(std::string)> callback) noexcept
|
|
1479
|
-
try
|
|
1480
|
-
{
|
|
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("");
|
|
1489
|
-
}
|
|
1490
|
-
catch (const hresult_error& ex)
|
|
1491
|
-
{
|
|
1492
|
-
callback(winrt::to_string(ex.message()).c_str());
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
void ReactNativeBlobUtil::closeStream(
|
|
1496
|
-
std::string streamId,
|
|
1497
|
-
std::function<void(std::string)> callback) noexcept
|
|
1498
|
-
try
|
|
1499
|
-
{
|
|
1500
|
-
auto stream{ m_streamMap.find(streamId)->second };
|
|
1501
|
-
stream.streamInstance.Close();
|
|
1502
|
-
m_streamMap.extract(streamId);
|
|
1503
|
-
callback("");
|
|
1504
|
-
}
|
|
1505
|
-
catch (const hresult_error& ex)
|
|
1506
|
-
{
|
|
1507
|
-
callback(winrt::to_string(ex.message()).c_str());
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
void ReactNativeBlobUtil::splitPath(const std::string& fullPath, winrt::hstring& directoryPath, winrt::hstring& fileName) noexcept
|
|
1512
|
-
{
|
|
1513
|
-
std::filesystem::path path{ fullPath };
|
|
1514
|
-
path.make_preferred();
|
|
1515
|
-
|
|
1516
|
-
directoryPath = path.has_parent_path() ? winrt::to_hstring(path.parent_path().c_str()) : L"";
|
|
1517
|
-
fileName = path.has_filename() ? winrt::to_hstring(path.filename().c_str()) : L"";
|
|
1518
|
-
}
|
|
1519
|
-
|
|
1520
|
-
void ReactNativeBlobUtil::splitPath(const std::wstring& fullPath, winrt::hstring& directoryPath, winrt::hstring& fileName) noexcept
|
|
1521
|
-
{
|
|
1522
|
-
std::filesystem::path path{ fullPath };
|
|
1523
|
-
path.make_preferred();
|
|
1524
|
-
|
|
1525
|
-
directoryPath = path.has_parent_path() ? winrt::to_hstring(path.parent_path().c_str()) : L"";
|
|
1526
|
-
fileName = path.has_filename() ? winrt::to_hstring(path.filename().c_str()) : L"";
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
winrt::Windows::Web::Http::
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
}
|
|
1678
|
-
|
|
1679
|
-
{
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
{
|
|
1692
|
-
|
|
1693
|
-
|
|
1
|
+
#include "pch.h"
|
|
2
|
+
#include "ReactNativeBlobUtil.h"
|
|
3
|
+
#include <winrt/Windows.ApplicationModel.Activation.h>
|
|
4
|
+
#include <winrt/Windows.Security.Cryptography.h>
|
|
5
|
+
#include <winrt/Windows.Security.Cryptography.Core.h>
|
|
6
|
+
#include <winrt/Windows.Storage.FileProperties.h>
|
|
7
|
+
#include <winrt/Windows.Storage.Streams.h>
|
|
8
|
+
#include <winrt/Windows.Storage.h>
|
|
9
|
+
#include <winrt/Windows.Web.Http.h>
|
|
10
|
+
#include <winrt/Windows.Web.Http.Headers.h>
|
|
11
|
+
#include <winrt/windows.web.http.filters.h>
|
|
12
|
+
#include <filesystem>
|
|
13
|
+
#include <sstream>
|
|
14
|
+
|
|
15
|
+
using namespace winrt;
|
|
16
|
+
using namespace winrt::Windows::ApplicationModel;
|
|
17
|
+
using namespace winrt::Windows::Storage;
|
|
18
|
+
using namespace winrt::Windows::Storage::Streams;
|
|
19
|
+
using namespace winrt::Windows::Foundation;
|
|
20
|
+
using namespace winrt::Windows::Security::Cryptography;
|
|
21
|
+
using namespace winrt::Windows::Security::Cryptography::Core;
|
|
22
|
+
using namespace std::chrono_literals;
|
|
23
|
+
|
|
24
|
+
CancellationDisposable::CancellationDisposable(IAsyncInfo const& async, std::function<void()>&& onCancel) noexcept
|
|
25
|
+
: m_async{ async }
|
|
26
|
+
, m_onCancel{ std::move(onCancel) }
|
|
27
|
+
{
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
CancellationDisposable::CancellationDisposable(CancellationDisposable&& other) noexcept
|
|
31
|
+
: m_async{ std::move(other.m_async) }
|
|
32
|
+
, m_onCancel{ std::move(other.m_onCancel) }
|
|
33
|
+
{
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
CancellationDisposable& CancellationDisposable::operator=(CancellationDisposable&& other) noexcept
|
|
37
|
+
{
|
|
38
|
+
if (this != &other)
|
|
39
|
+
{
|
|
40
|
+
CancellationDisposable temp{ std::move(*this) };
|
|
41
|
+
m_async = std::move(other.m_async);
|
|
42
|
+
m_onCancel = std::move(other.m_onCancel);
|
|
43
|
+
}
|
|
44
|
+
return *this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
CancellationDisposable::~CancellationDisposable() noexcept
|
|
48
|
+
{
|
|
49
|
+
Cancel();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
void CancellationDisposable::Cancel() noexcept
|
|
53
|
+
{
|
|
54
|
+
if (m_async)
|
|
55
|
+
{
|
|
56
|
+
if (m_async.Status() == AsyncStatus::Started)
|
|
57
|
+
{
|
|
58
|
+
m_async.Cancel();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (m_onCancel)
|
|
62
|
+
{
|
|
63
|
+
m_onCancel();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
TaskCancellationManager::~TaskCancellationManager() noexcept
|
|
69
|
+
{
|
|
70
|
+
// Do the explicit cleaning to make sure that CancellationDisposable
|
|
71
|
+
// destructors run while this instance still has valid fields because
|
|
72
|
+
// they are used by the onCancel callback.
|
|
73
|
+
// We also want to clear the m_pendingTasks before running the
|
|
74
|
+
// CancellationDisposable destructors since they touch the m_pendingTasks.
|
|
75
|
+
std::map<TaskId, CancellationDisposable> pendingTasks;
|
|
76
|
+
{
|
|
77
|
+
std::scoped_lock lock{ m_mutex };
|
|
78
|
+
pendingTasks = std::move(m_pendingTasks);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
IAsyncAction TaskCancellationManager::Add(TaskId taskId, IAsyncAction const& asyncAction) noexcept
|
|
84
|
+
{
|
|
85
|
+
std::scoped_lock lock{ m_mutex };
|
|
86
|
+
m_pendingTasks.try_emplace(taskId, asyncAction, [this, taskId]()
|
|
87
|
+
{
|
|
88
|
+
Cancel(taskId);
|
|
89
|
+
});
|
|
90
|
+
return asyncAction;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
void TaskCancellationManager::Cancel(TaskId taskId) noexcept
|
|
94
|
+
{
|
|
95
|
+
// The destructor of the token does the cancellation. We must do it outside of lock.
|
|
96
|
+
CancellationDisposable token;
|
|
97
|
+
|
|
98
|
+
{
|
|
99
|
+
std::scoped_lock lock{ m_mutex };
|
|
100
|
+
if (!m_pendingTasks.empty())
|
|
101
|
+
{
|
|
102
|
+
if (auto it = m_pendingTasks.find(taskId); it != m_pendingTasks.end())
|
|
103
|
+
{
|
|
104
|
+
token = std::move(it->second);
|
|
105
|
+
m_pendingTasks.erase(it);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
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
|
|
147
|
+
{
|
|
148
|
+
m_reactContext = reactContext;
|
|
149
|
+
}
|
|
150
|
+
|
|
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)
|
|
244
|
+
{
|
|
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
|
+
}
|
|
255
|
+
|
|
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
|
|
261
|
+
{
|
|
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
|
+
|
|
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)
|
|
283
|
+
{
|
|
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
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
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)
|
|
354
|
+
{
|
|
355
|
+
promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "EUNSPECIFIED: " + winrt::to_string(ex.message()) + "; " + path });
|
|
356
|
+
}
|
|
357
|
+
|
|
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
|
|
364
|
+
{
|
|
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());
|
|
393
|
+
|
|
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 });
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
// writeStream
|
|
403
|
+
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
|
|
409
|
+
{
|
|
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);
|
|
449
|
+
}
|
|
450
|
+
catch (const hresult_error& ex)
|
|
451
|
+
{
|
|
452
|
+
callback("EUNSPECIFIED", "Failed to create write stream at path '" + path + "'; " + winrt::to_string(ex.message().c_str()), "");
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
// writeChunk
|
|
457
|
+
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)
|
|
482
|
+
{
|
|
483
|
+
callback(winrt::to_string(ex.message().c_str()));
|
|
484
|
+
}
|
|
485
|
+
|
|
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)
|
|
505
|
+
{
|
|
506
|
+
callback(winrt::to_string(ex.message().c_str()));
|
|
507
|
+
}
|
|
508
|
+
|
|
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
|
|
517
|
+
{
|
|
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)
|
|
624
|
+
{
|
|
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
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
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)
|
|
665
|
+
{
|
|
666
|
+
promise.Reject(winrt::Microsoft::ReactNative::ReactError{ "EUNSPECIFIED", "Error creating folder " + path + ", error: " + winrt::to_string(ex.message().c_str()) });
|
|
667
|
+
}
|
|
668
|
+
|
|
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)
|
|
703
|
+
{
|
|
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
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
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
|
|
727
|
+
{
|
|
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
|
+
}
|
|
734
|
+
|
|
735
|
+
winrt::hstring directoryPath, fileName;
|
|
736
|
+
splitPath(path, directoryPath, fileName);
|
|
737
|
+
|
|
738
|
+
StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
|
|
739
|
+
StorageFile file{ co_await folder.GetFileAsync(fileName) };
|
|
740
|
+
|
|
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
|
+
}
|
|
747
|
+
|
|
748
|
+
CryptographyCore::HashAlgorithmProvider provider{ search->second() };
|
|
749
|
+
Streams::IBuffer buffer{ co_await FileIO::ReadBufferAsync(file) };
|
|
750
|
+
|
|
751
|
+
auto hashedBuffer{ provider.HashData(buffer) };
|
|
752
|
+
std::wstring result{ Cryptography::CryptographicBuffer::EncodeToHexString(hashedBuffer) };
|
|
753
|
+
|
|
754
|
+
promise.Resolve(result);
|
|
755
|
+
}
|
|
756
|
+
catch (const hresult_error& ex)
|
|
757
|
+
{
|
|
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
|
+
}
|
|
773
|
+
|
|
774
|
+
|
|
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);
|
|
783
|
+
|
|
784
|
+
StorageFolder targetDirectory{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
|
|
785
|
+
|
|
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
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
// mv
|
|
809
|
+
winrt::fire_and_forget ReactNativeBlobUtil::mv(
|
|
810
|
+
std::string src, // from
|
|
811
|
+
std::string dest, // to
|
|
812
|
+
std::function<void(std::string)> callback) noexcept
|
|
813
|
+
{
|
|
814
|
+
try
|
|
815
|
+
{
|
|
816
|
+
winrt::hstring srcDirectoryPath, srcFileName;
|
|
817
|
+
splitPath(src, srcDirectoryPath, srcFileName);
|
|
818
|
+
|
|
819
|
+
winrt::hstring destDirectoryPath, destFileName;
|
|
820
|
+
splitPath(dest, destDirectoryPath, destFileName);
|
|
821
|
+
|
|
822
|
+
StorageFolder srcFolder{ co_await StorageFolder::GetFolderFromPathAsync(srcDirectoryPath) };
|
|
823
|
+
StorageFolder destFolder{ co_await StorageFolder::GetFolderFromPathAsync(destDirectoryPath) };
|
|
824
|
+
StorageFile file{ co_await srcFolder.GetFileAsync(srcFileName) };
|
|
825
|
+
|
|
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
|
+
}
|
|
842
|
+
|
|
843
|
+
|
|
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
|
|
850
|
+
{
|
|
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("");
|
|
864
|
+
}
|
|
865
|
+
catch (const hresult_error& ex)
|
|
866
|
+
{
|
|
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());
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
// exists
|
|
877
|
+
void ReactNativeBlobUtil::exists(
|
|
878
|
+
std::string path,
|
|
879
|
+
std::function<void(bool, bool)> callback) noexcept
|
|
880
|
+
{
|
|
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);
|
|
886
|
+
}
|
|
887
|
+
|
|
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
|
|
894
|
+
{
|
|
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);
|
|
911
|
+
}
|
|
912
|
+
catch (const hresult_error& ex)
|
|
913
|
+
{
|
|
914
|
+
callback(winrt::to_string(ex.message()), false);
|
|
915
|
+
}
|
|
916
|
+
|
|
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
|
|
923
|
+
{
|
|
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);
|
|
947
|
+
}
|
|
948
|
+
catch (...)
|
|
949
|
+
{
|
|
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);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
|
|
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
|
|
961
|
+
{
|
|
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);
|
|
986
|
+
}
|
|
987
|
+
catch (const hresult_error& ex)
|
|
988
|
+
{
|
|
989
|
+
winrt::Microsoft::ReactNative::JSValueObject emptyObject;
|
|
990
|
+
callback(winrt::to_string(ex.message()).c_str(), emptyObject);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
|
|
994
|
+
// df
|
|
995
|
+
winrt::fire_and_forget ReactNativeBlobUtil::df(
|
|
996
|
+
std::function<void(std::string, winrt::Microsoft::ReactNative::JSValueObject&)> callback) noexcept
|
|
997
|
+
try
|
|
998
|
+
{
|
|
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);
|
|
1006
|
+
}
|
|
1007
|
+
catch (...)
|
|
1008
|
+
{
|
|
1009
|
+
winrt::Microsoft::ReactNative::JSValueObject emptyObject;
|
|
1010
|
+
callback("Failed to get storage usage.", emptyObject);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
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
|
|
1021
|
+
{
|
|
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);
|
|
1040
|
+
}
|
|
1041
|
+
catch (...)
|
|
1042
|
+
{
|
|
1043
|
+
promise.Reject("Unable to slice file");
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
IAsyncAction setTimeout(std::chrono::seconds time) {
|
|
1048
|
+
co_await time;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
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
|
|
1059
|
+
{
|
|
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
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
|
|
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
|
|
1241
|
+
{
|
|
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
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
|
|
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);
|
|
1450
|
+
}
|
|
1451
|
+
|
|
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);
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
// cancelRequest
|
|
1463
|
+
void ReactNativeBlobUtil::cancelRequest(
|
|
1464
|
+
std::string taskId,
|
|
1465
|
+
std::function<void(std::string, std::string)> callback) noexcept
|
|
1466
|
+
try
|
|
1467
|
+
{
|
|
1468
|
+
m_tasks.Cancel(taskId);
|
|
1469
|
+
callback("", taskId);
|
|
1470
|
+
}
|
|
1471
|
+
catch (const hresult_error& ex)
|
|
1472
|
+
{
|
|
1473
|
+
callback(winrt::to_string(ex.message()), "");
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
winrt::fire_and_forget ReactNativeBlobUtil::removeSession(
|
|
1477
|
+
winrt::Microsoft::ReactNative::JSValueArray paths,
|
|
1478
|
+
std::function<void(std::string)> callback) noexcept
|
|
1479
|
+
try
|
|
1480
|
+
{
|
|
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("");
|
|
1489
|
+
}
|
|
1490
|
+
catch (const hresult_error& ex)
|
|
1491
|
+
{
|
|
1492
|
+
callback(winrt::to_string(ex.message()).c_str());
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
void ReactNativeBlobUtil::closeStream(
|
|
1496
|
+
std::string streamId,
|
|
1497
|
+
std::function<void(std::string)> callback) noexcept
|
|
1498
|
+
try
|
|
1499
|
+
{
|
|
1500
|
+
auto stream{ m_streamMap.find(streamId)->second };
|
|
1501
|
+
stream.streamInstance.Close();
|
|
1502
|
+
m_streamMap.extract(streamId);
|
|
1503
|
+
callback("");
|
|
1504
|
+
}
|
|
1505
|
+
catch (const hresult_error& ex)
|
|
1506
|
+
{
|
|
1507
|
+
callback(winrt::to_string(ex.message()).c_str());
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
|
|
1511
|
+
void ReactNativeBlobUtil::splitPath(const std::string& fullPath, winrt::hstring& directoryPath, winrt::hstring& fileName) noexcept
|
|
1512
|
+
{
|
|
1513
|
+
std::filesystem::path path{ fullPath };
|
|
1514
|
+
path.make_preferred();
|
|
1515
|
+
|
|
1516
|
+
directoryPath = path.has_parent_path() ? winrt::to_hstring(path.parent_path().c_str()) : L"";
|
|
1517
|
+
fileName = path.has_filename() ? winrt::to_hstring(path.filename().c_str()) : L"";
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
void ReactNativeBlobUtil::splitPath(const std::wstring& fullPath, winrt::hstring& directoryPath, winrt::hstring& fileName) noexcept
|
|
1521
|
+
{
|
|
1522
|
+
std::filesystem::path path{ fullPath };
|
|
1523
|
+
path.make_preferred();
|
|
1524
|
+
|
|
1525
|
+
directoryPath = path.has_parent_path() ? winrt::to_hstring(path.parent_path().c_str()) : L"";
|
|
1526
|
+
fileName = path.has_filename() ? winrt::to_hstring(path.filename().c_str()) : L"";
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
void ReactNativeBlobUtil::addListener(std::string eventName) noexcept
|
|
1530
|
+
{
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
void ReactNativeBlobUtil::removeListeners(double count) noexcept
|
|
1534
|
+
{
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
winrt::Windows::Foundation::IAsyncAction ReactNativeBlobUtil::ProcessRequestAsync(
|
|
1538
|
+
const std::string& taskId,
|
|
1539
|
+
const winrt::Windows::Web::Http::Filters::HttpBaseProtocolFilter& filter,
|
|
1540
|
+
winrt::Windows::Web::Http::HttpRequestMessage& httpRequestMessage,
|
|
1541
|
+
ReactNativeBlobUtilConfig& config,
|
|
1542
|
+
std::function<void(std::string, std::string, std::string)> callback,
|
|
1543
|
+
std::string& error) noexcept
|
|
1544
|
+
try
|
|
1545
|
+
{
|
|
1546
|
+
winrt::Windows::Web::Http::HttpClient httpClient{filter};
|
|
1547
|
+
|
|
1548
|
+
winrt::Windows::Web::Http::HttpResponseMessage response{ co_await httpClient.SendRequestAsync(httpRequestMessage, winrt::Windows::Web::Http::HttpCompletionOption::ResponseHeadersRead) };
|
|
1549
|
+
|
|
1550
|
+
ReactNativeBlobUtilState eventState;
|
|
1551
|
+
|
|
1552
|
+
auto status{ static_cast<int>(response.StatusCode()) };
|
|
1553
|
+
if (config.followRedirect) {
|
|
1554
|
+
while (status >= 300 && status < 400) {
|
|
1555
|
+
auto redirect{ response.Headers().Location().ToString() };
|
|
1556
|
+
eventState.redirects.push_back(Microsoft::ReactNative::JSValue(winrt::to_string(redirect)));
|
|
1557
|
+
httpRequestMessage.RequestUri(Uri{ redirect });
|
|
1558
|
+
response = co_await httpClient.SendRequestAsync(httpRequestMessage, winrt::Windows::Web::Http::HttpCompletionOption::ResponseHeadersRead);
|
|
1559
|
+
status = static_cast<int>(response.StatusCode());
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
eventState.status = static_cast<int>(response.StatusCode());
|
|
1564
|
+
|
|
1565
|
+
for (const auto header : response.Content().Headers().GetView()) {
|
|
1566
|
+
eventState.headers[winrt::to_string(header.Key())] = winrt::to_string(header.Value());
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
if (response.Content().Headers().ContentType() != nullptr) {
|
|
1570
|
+
eventState.respType = winrt::to_string(response.Content().Headers().ContentType().ToString());
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
eventState.state = winrt::to_string(response.ReasonPhrase());
|
|
1574
|
+
|
|
1575
|
+
m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", L"ReactNativeBlobUtilState",
|
|
1576
|
+
Microsoft::ReactNative::JSValueObject{
|
|
1577
|
+
{ "taskId", taskId },
|
|
1578
|
+
{ "state", eventState.state },
|
|
1579
|
+
{ "headers", std::move(eventState.headers) },
|
|
1580
|
+
{ "redirects", std::move(eventState.redirects) },
|
|
1581
|
+
{ "respType", eventState.respType },
|
|
1582
|
+
{ "status", eventState.status },
|
|
1583
|
+
{ "timeout", false },
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
IReference<uint64_t> contentLength{ response.Content().Headers().ContentLength() };
|
|
1587
|
+
|
|
1588
|
+
IOutputStream outputStream;
|
|
1589
|
+
bool writeToFile{ config.fileCache || !config.path.empty() };
|
|
1590
|
+
|
|
1591
|
+
if (writeToFile)
|
|
1592
|
+
{
|
|
1593
|
+
if (config.path.empty())
|
|
1594
|
+
{
|
|
1595
|
+
config.path = winrt::to_string(ApplicationData::Current().TemporaryFolder().Path()) + "\\ReactNativeBlobUtilTmp_" + taskId;
|
|
1596
|
+
if (config.appendExt.length() > 0)
|
|
1597
|
+
{
|
|
1598
|
+
config.path += "." + config.appendExt;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
std::filesystem::path path{ config.path };
|
|
1602
|
+
StorageFolder storageFolder{ co_await StorageFolder::GetFolderFromPathAsync( path.parent_path().wstring()) };
|
|
1603
|
+
StorageFile storageFile{ co_await storageFolder.CreateFileAsync(path.filename().wstring(), CreationCollisionOption::FailIfExists) };
|
|
1604
|
+
IRandomAccessStream stream{ co_await storageFile.OpenAsync(FileAccessMode::ReadWrite) };
|
|
1605
|
+
outputStream = stream.GetOutputStreamAt(0) ;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
auto contentStream{ co_await response.Content().ReadAsInputStreamAsync() };
|
|
1609
|
+
Buffer buffer{ 10 * 1024 };
|
|
1610
|
+
uint64_t read{ 0 };
|
|
1611
|
+
uint64_t totalRead{ 0 };
|
|
1612
|
+
|
|
1613
|
+
ReactNativeBlobUtilProgressConfig progressInfo;
|
|
1614
|
+
uint64_t progressInterval{ 0 };
|
|
1615
|
+
|
|
1616
|
+
std::stringstream chunkStream;
|
|
1617
|
+
std::stringstream resultOutput;
|
|
1618
|
+
|
|
1619
|
+
std::string readContents{""};
|
|
1620
|
+
|
|
1621
|
+
auto exists{ downloadProgressMap.find(taskId) };
|
|
1622
|
+
if (exists != downloadProgressMap.end() && contentLength.Type() == PropertyType::UInt64) {
|
|
1623
|
+
progressInfo = downloadProgressMap[taskId];
|
|
1624
|
+
|
|
1625
|
+
if (progressInfo.count > -1) {
|
|
1626
|
+
progressInterval = contentLength.Value() / 100 * progressInfo.count;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
int64_t initialProgressTime{ winrt::clock::now().time_since_epoch().count() / 10000 };
|
|
1631
|
+
int64_t currentProgressTime;
|
|
1632
|
+
|
|
1633
|
+
for (;;)
|
|
1634
|
+
{
|
|
1635
|
+
buffer.Length(0);
|
|
1636
|
+
auto readBuffer{ co_await contentStream.ReadAsync(buffer, buffer.Capacity(), InputStreamOptions::None) };
|
|
1637
|
+
|
|
1638
|
+
read += readBuffer.Length();
|
|
1639
|
+
totalRead += read;
|
|
1640
|
+
|
|
1641
|
+
if (readBuffer.Length() == 0)
|
|
1642
|
+
{
|
|
1643
|
+
break;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
readContents = winrt::to_string(CryptographicBuffer::EncodeToBase64String(readBuffer));
|
|
1647
|
+
|
|
1648
|
+
if (writeToFile) {
|
|
1649
|
+
co_await outputStream.WriteAsync(readBuffer);
|
|
1650
|
+
}
|
|
1651
|
+
else {
|
|
1652
|
+
resultOutput << readContents;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
if (progressInfo.count > -1 || progressInfo.interval > -1) {
|
|
1656
|
+
chunkStream << readContents;
|
|
1657
|
+
|
|
1658
|
+
currentProgressTime = winrt::clock::now().time_since_epoch().count() / 10000;
|
|
1659
|
+
if ((currentProgressTime - initialProgressTime >= progressInfo.interval && progressInfo.interval > -1) ||
|
|
1660
|
+
(totalRead >= progressInterval && progressInfo.count > -1)) {
|
|
1661
|
+
m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", L"ReactNativeBlobUtilProgress",
|
|
1662
|
+
Microsoft::ReactNative::JSValueObject{
|
|
1663
|
+
{ "taskId", taskId },
|
|
1664
|
+
{ "written", int64_t(totalRead) },
|
|
1665
|
+
{ "total", contentLength.Type() == PropertyType::UInt64 ?
|
|
1666
|
+
Microsoft::ReactNative::JSValue(contentLength.Value()) :
|
|
1667
|
+
Microsoft::ReactNative::JSValue{nullptr} },
|
|
1668
|
+
{ "chunk", chunkStream.str() },
|
|
1669
|
+
});
|
|
1670
|
+
chunkStream.clear();
|
|
1671
|
+
initialProgressTime = winrt::clock::now().time_since_epoch().count() / 10000;
|
|
1672
|
+
if (progressInfo.count > -1) {
|
|
1673
|
+
read = 0;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
if (writeToFile) {
|
|
1680
|
+
callback("", "path", config.path);
|
|
1681
|
+
}
|
|
1682
|
+
else {
|
|
1683
|
+
callback("", "result", resultOutput.str());
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
catch (const hresult_error& ex)
|
|
1687
|
+
{
|
|
1688
|
+
error = winrt::to_string(ex.message().c_str());
|
|
1689
|
+
//callback(winrt::to_string(ex.message().c_str()), "error", "");
|
|
1690
|
+
}
|
|
1691
|
+
catch (...) {
|
|
1692
|
+
co_return;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
|
|
1696
|
+
ReactNativeBlobUtilStream::ReactNativeBlobUtilStream(Streams::IRandomAccessStream& _streamInstance, EncodingOptions _encoding) noexcept
|
|
1697
|
+
: streamInstance{ std::move(_streamInstance) }
|
|
1698
|
+
, encoding{ _encoding }
|
|
1699
|
+
{
|
|
1700
|
+
}
|
|
1701
|
+
|