Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix undefined ContentLenght and support BinaryStreamOnly in Windows #1198

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 78 additions & 57 deletions windows/RNFS/RNFSManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,8 @@ IAsyncAction RNFSManager::ProcessDownloadRequestAsync(RN::ReactPromise<RN::JSVal
try
{
HttpResponseMessage response = co_await m_httpClient.SendRequestAsync(request, HttpCompletionOption::ResponseHeadersRead);
IReference<uint64_t> contentLength{ response.Content().Headers().ContentLength() };
auto contentLength = response.Content().Headers().ContentLength();

{
RN::JSValueObject headersMap;
for (auto const& header : response.Headers())
Expand All @@ -944,7 +945,7 @@ IAsyncAction RNFSManager::ProcessDownloadRequestAsync(RN::ReactPromise<RN::JSVal
RN::JSValueObject{
{ "jobId", jobId },
{ "statusCode", (int)response.StatusCode() },
{ "contentLength", contentLength.Type() == PropertyType::UInt64 ? RN::JSValue(contentLength.Value()) : RN::JSValue{nullptr} },
{ "contentLength", contentLength != nullptr ? RN::JSValue(contentLength.Value()) : RN::JSValue{nullptr} },
{ "headers", std::move(headersMap) },
});
}
Expand All @@ -959,8 +960,8 @@ IAsyncAction RNFSManager::ProcessDownloadRequestAsync(RN::ReactPromise<RN::JSVal
IOutputStream outputStream{ stream.GetOutputStreamAt(0) };

auto contentStream = co_await response.Content().ReadAsInputStreamAsync();
auto contentLengthForProgress = contentLength.Type() == PropertyType::UInt64 ? contentLength.Value() : -1;
auto contentLengthForProgress = contentLength != nullptr ? contentLength.Value() : -1;

Buffer buffer{ 8 * 1024 };
uint32_t read = 0;
int64_t initialProgressTime{ winrt::clock::now().time_since_epoch().count() / 10000 };
Expand Down Expand Up @@ -988,7 +989,7 @@ IAsyncAction RNFSManager::ProcessDownloadRequestAsync(RN::ReactPromise<RN::JSVal
m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", L"DownloadProgress",
RN::JSValueObject{
{ "jobId", jobId },
{ "contentLength", contentLength.Type() == PropertyType::UInt64 ? RN::JSValue(contentLength.Value()) : RN::JSValue{nullptr} },
{ "contentLength", contentLength != nullptr ? RN::JSValue(contentLength.Value()) : RN::JSValue{nullptr} },
{ "bytesWritten", totalRead },
});
initialProgressTime = winrt::clock::now().time_since_epoch().count() / 10000;
Expand All @@ -999,7 +1000,7 @@ IAsyncAction RNFSManager::ProcessDownloadRequestAsync(RN::ReactPromise<RN::JSVal
m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", L"DownloadProgress",
RN::JSValueObject{
{ "jobId", jobId },
{ "contentLength", contentLength.Type() == PropertyType::UInt64 ? RN::JSValue(contentLength.Value()) : RN::JSValue{nullptr} },
{ "contentLength", contentLength != nullptr ? RN::JSValue(contentLength.Value()) : RN::JSValue{nullptr} },
{ "bytesWritten", totalRead },
});
}
Expand All @@ -1010,7 +1011,7 @@ IAsyncAction RNFSManager::ProcessDownloadRequestAsync(RN::ReactPromise<RN::JSVal
m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", L"DownloadProgress",
RN::JSValueObject{
{ "jobId", jobId },
{ "contentLength", contentLength.Type() == PropertyType::UInt64 ? RN::JSValue(contentLength.Value()) : RN::JSValue{nullptr} },
{ "contentLength", contentLength != nullptr ? RN::JSValue(contentLength.Value()) : RN::JSValue{nullptr} },
{ "bytesWritten", totalRead },
});
}
Expand Down Expand Up @@ -1042,87 +1043,107 @@ IAsyncAction RNFSManager::ProcessUploadRequestAsync(RN::ReactPromise<RN::JSValue
{
try
{
winrt::hstring boundary{ L"-----" };
std::string toUrl{ options["toUrl"].AsString() };
std::wstring URLForURI(toUrl.begin(), toUrl.end());
Uri uri{ URLForURI };

winrt::Windows::Web::Http::HttpRequestMessage requestMessage{ httpMethod, uri };
winrt::Windows::Web::Http::HttpMultipartFormDataContent requestContent{ boundary };

auto const& headers{ options["headers"].AsObject() };

for (auto const& entry : headers)
{
if (!requestMessage.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString())))
{
requestContent.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString()));
}
requestMessage.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString()));
}

auto const& fields{ options["fields"].AsObject() }; // placed in the header
std::stringstream attempt;
attempt << "form-data";
for (auto const& field : fields)
if (options["binaryStreamOnly"].AsBoolean()) // If binaryStreamOnly is true, then upload raw binary
{
attempt << "; " << field.first << "=" << field.second.AsString();
for (const auto& fileInfo : files)
{
auto filepath{ fileInfo.AsObject()["filepath"].AsString() }; // accessing the file

try
{
winrt::hstring directoryPath, fileName;
splitPath(filepath, directoryPath, fileName);
StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
StorageFile file{ co_await folder.GetFileAsync(fileName) };

HttpBufferContent bufferContent{ co_await FileIO::ReadBufferAsync(file) };
requestMessage.Content(bufferContent);
}
catch (...)
{
continue;
}
}
}
else
{
winrt::hstring boundary{ L"-----" };
winrt::Windows::Web::Http::HttpMultipartFormDataContent requestContent{ boundary };

requestContent.Headers().ContentDisposition(Headers::HttpContentDispositionHeaderValue::Parse(winrt::to_hstring(attempt.str())));
for (auto const& entry : headers)
{
if (!requestMessage.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString())))
{
requestContent.Headers().TryAppendWithoutValidation(winrt::to_hstring(entry.first), winrt::to_hstring(entry.second.AsString()));
}
}

m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", L"UploadBegin",
RN::JSValueObject{
{ "jobId", jobId },
});
auto const& fields{ options["fields"].AsObject() };
std::stringstream attempt;
attempt << "form-data";
for (auto const& field : fields)
{
attempt << "; " << field.first << "=" << field.second.AsString();
}

uint64_t totalUploaded{ 0 };
requestContent.Headers().ContentDisposition(Headers::HttpContentDispositionHeaderValue::Parse(winrt::to_hstring(attempt.str())));

for (const auto& fileInfo : files)
{
auto const& fileObj{ fileInfo.AsObject() };
auto name{ winrt::to_hstring(fileObj["name"].AsString()) }; // name to be sent via http request
auto filename{ winrt::to_hstring(fileObj["filename"].AsString()) }; // filename to be sent via http request
auto filepath{ fileObj["filepath"].AsString()}; // accessing the file
uint64_t totalUploaded{ 0 };

try
for (const auto& fileInfo : files)
{
winrt::hstring directoryPath, fileName;
splitPath(filepath, directoryPath, fileName);
StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
StorageFile file{ co_await folder.GetFileAsync(fileName) };
auto properties{ co_await file.GetBasicPropertiesAsync() };
auto const& fileObj{ fileInfo.AsObject() };
auto name{ winrt::to_hstring(fileObj["name"].AsString()) };
auto filename{ winrt::to_hstring(fileObj["filename"].AsString()) };
auto filepath{ fileObj["filepath"].AsString() };

HttpBufferContent entry{ co_await FileIO::ReadBufferAsync(file) };
requestContent.Add(entry, name, filename);
try
{
winrt::hstring directoryPath, fileName;
splitPath(filepath, directoryPath, fileName);
StorageFolder folder{ co_await StorageFolder::GetFolderFromPathAsync(directoryPath) };
StorageFile file{ co_await folder.GetFileAsync(fileName) };
auto properties{ co_await file.GetBasicPropertiesAsync() };

totalUploaded += properties.Size();
m_reactContext.CallJSFunction(L"RCTDeviceEventEmitter", L"emit", L"UploadProgress",
RN::JSValueObject{
{ "jobId", jobId },
{ "totalBytesExpectedToSend", totalUploadSize }, // The total number of bytes that will be sent to the server
{ "totalBytesSent", totalUploaded },
});
}
catch (...)
{
continue;
HttpBufferContent entry{ co_await FileIO::ReadBufferAsync(file) };
requestContent.Add(entry, name, filename);

totalUploaded += properties.Size();
}
catch (...)
{
continue;
}
}

requestMessage.Content(requestContent);
}

requestMessage.Content(requestContent);
HttpResponseMessage response = co_await m_httpClient.SendRequestAsync(requestMessage, HttpCompletionOption::ResponseHeadersRead);

auto statusCode{ std::to_string(int(response.StatusCode())) };
auto resultHeaders{ winrt::to_string(response.Headers().ToString()) };
auto resultContent{ winrt::to_string(co_await response.Content().ReadAsStringAsync()) };

promise.Resolve(RN::JSValueObject
{
{ "jobId", jobId },
{ "statusCode", statusCode},
{ "headers", resultHeaders},
{ "body", resultContent},
});
{
{ "jobId", jobId },
{ "statusCode", statusCode },
{ "headers", resultHeaders },
{ "body", resultContent },
});
}
catch (winrt::hresult_canceled const& ex)
{
Expand Down