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