Add possibility to download files synchronously.

GitOrigin-RevId: 2b633fcc06a8d8770a6fb31a0d0bd9dac971df9e
This commit is contained in:
levlam 2019-03-27 21:43:46 +03:00
parent ad2690b2a6
commit db66ddf0be
8 changed files with 83 additions and 13 deletions

View File

@ -118,7 +118,7 @@ namespace TdApp
{
var args = command.Split(" ".ToCharArray(), 2);
AcceptCommand(command);
_client.Send(new TdApi.DownloadFile(Int32.Parse(args[1]), 1, 0, 0), _handler);
_client.Send(new TdApi.DownloadFile(Int32.Parse(args[1]), 1, 0, 0, false), _handler);
}
else if (command.StartsWith("bench"))
{

View File

@ -3099,12 +3099,14 @@ resetAllNotificationSettings = Ok;
setPinnedChats chat_ids:vector<int53> = Ok;
//@description Asynchronously downloads a file from the cloud. updateFile will be used to notify about the download progress and successful completion of the download. Returns file state just after the download has been started
//@description Downloads a file from the cloud. Download progress and completion of the download will be notified through updateFile updates
//@file_id Identifier of the file to download
//@priority Priority of the download (1-32). The higher the priority, the earlier the file will be downloaded. If the priorities of two files are equal, then the last one for which downloadFile was called will be downloaded first
//@offset File will be downloaded starting from that offset in bytes first. Supposed to be used for streaming
//@limit Download will be automatically cancelled when at least limit bytes are downloaded starting from the specified offset; use 0 to download without limit
downloadFile file_id:int32 priority:int32 offset:int32 limit:int32 = File;
//@synchronous If false, this request returns file state just after the download has been started. If true, this request returns file state only after
//-the download has succeeded, has failed, has been cancelled or a new downloadFile request with different offset/limit parameters was sent
downloadFile file_id:int32 priority:int32 offset:int32 limit:int32 synchronous:Bool = File;
//@description Returns file downloaded prefix size from a given offset @file_id Identifier of the file @offset Offset from which downloaded prefix size should be calculated
getFileDownloadedPrefixSize file_id:int32 offset:int32 = Count;

Binary file not shown.

View File

@ -3915,9 +3915,11 @@ class Td::DownloadFileCallback : public FileManager::DownloadCallback {
}
void on_download_ok(FileId file_id) override {
send_closure(G()->td(), &Td::on_file_download_finished, file_id);
}
void on_download_error(FileId file_id, Status error) override {
send_closure(G()->td(), &Td::on_file_download_finished, file_id);
}
};
@ -5734,18 +5736,67 @@ void Td::on_request(uint64 id, const td_api::downloadFile &request) {
if (!(1 <= priority && priority <= 32)) {
return send_error_raw(id, 5, "Download priority must be in [1;32] range");
}
if (request.offset_ < 0) {
auto offset = request.offset_;
if (offset < 0) {
return send_error_raw(id, 5, "Download offset must be non-negative");
}
file_manager_->download(FileId(request.file_id_, 0), download_file_callback_, priority, request.offset_,
request.limit_);
auto limit = request.limit_;
if (limit < 0) {
return send_error_raw(id, 5, "Download limit must be non-negative");
}
auto file = file_manager_->get_file_object(FileId(request.file_id_, 0), false);
if (file->id_ == 0) {
FileId file_id(request.file_id_, 0);
auto file_view = file_manager_->get_file_view(file_id);
if (file_view.empty()) {
return send_error_raw(id, 400, "Invalid file id");
}
send_closure(actor_id(this), &Td::send_result, id, std::move(file));
auto info_it = pending_file_downloads_.find(file_id);
DownloadInfo *info = info_it == pending_file_downloads_.end() ? nullptr : &info_it->second;
if (info != nullptr && (offset != info->offset || limit != info->limit)) {
// we can't have two pending requests with different offset and limit, so cancel all previous requests
for (auto request_id : info->request_ids) {
send_closure(actor_id(this), &Td::send_error, request_id,
Status::Error(200, "Cancelled by another downloadFile request"));
}
info->request_ids.clear();
}
if (request.synchronous_) {
if (info == nullptr) {
info = &pending_file_downloads_[file_id];
}
info->offset = offset;
info->limit = limit;
info->request_ids.push_back(id);
}
file_manager_->download(file_id, download_file_callback_, priority, offset, limit);
if (!request.synchronous_) {
send_closure(actor_id(this), &Td::send_result, id, file_manager_->get_file_object(file_id, false));
}
}
void Td::on_file_download_finished(FileId file_id) {
auto it = pending_file_downloads_.find(file_id);
if (it == pending_file_downloads_.end()) {
return;
}
for (auto id : it->second.request_ids) {
// there was send_closure to call this function
auto file_object = file_manager_->get_file_object(file_id, false);
CHECK(file_object != nullptr);
auto download_offset = file_object->local_->download_offset_;
auto downloaded_size = file_object->local_->downloaded_prefix_size_;
auto file_size = file_object->size_;
if (file_object->local_->is_downloading_completed_ ||
(download_offset <= it->second.offset && download_offset + downloaded_size >= it->second.offset &&
((file_size != 0 && download_offset + downloaded_size == file_size) ||
download_offset + downloaded_size - it->second.offset >= it->second.limit))) {
send_result(id, std::move(file_object));
} else {
send_error_impl(id, td_api::make_object<td_api::error>(400, "File download has failed or was cancelled"));
}
}
pending_file_downloads_.erase(it);
}
void Td::on_request(uint64 id, const td_api::cancelDownloadFile &request) {

View File

@ -6,6 +6,7 @@
//
#pragma once
#include "td/telegram/files/FileId.h"
#include "td/telegram/net/MtprotoHeader.h"
#include "td/telegram/net/NetQuery.h"
#include "td/telegram/StateManager.h"
@ -275,6 +276,13 @@ class Td final : public NetQueryCallback {
TermsOfService pending_terms_of_service_;
struct DownloadInfo {
int32 offset = -1;
int32 limit = -1;
vector<uint64> request_ids;
};
std::unordered_map<FileId, DownloadInfo, FileIdHash> pending_file_downloads_;
vector<std::pair<uint64, td_api::object_ptr<td_api::Function>>> pending_preauthentication_requests_;
template <class T>
@ -303,6 +311,8 @@ class Td final : public NetQueryCallback {
void clear_requests();
void on_file_download_finished(FileId file_id);
static bool is_internal_config_option(Slice name);
void on_config_option_updated(const string &name);

View File

@ -2371,7 +2371,7 @@ class CliClient final : public Actor {
send_request(td_api::make_object<td_api::getMapThumbnailFile>(
as_location(latitude, longitude), to_integer<int32>(zoom), to_integer<int32>(width),
to_integer<int32>(height), to_integer<int32>(scale), as_chat_id(chat_id)));
} else if (op == "df" || op == "DownloadFile" || op == "dff") {
} else if (op == "df" || op == "DownloadFile" || op == "dff" || op == "dfs") {
string file_id;
string priority;
string offset;
@ -2386,8 +2386,8 @@ class CliClient final : public Actor {
int32 max_file_id = as_file_id(file_id);
int32 min_file_id = (op == "dff" ? 1 : max_file_id);
for (int32 i = min_file_id; i <= max_file_id; i++) {
send_request(td_api::make_object<td_api::downloadFile>(i, to_integer<int32>(priority),
to_integer<int32>(offset), to_integer<int32>(limit)));
send_request(td_api::make_object<td_api::downloadFile>(
i, to_integer<int32>(priority), to_integer<int32>(offset), to_integer<int32>(limit), op == "dfs"));
}
} else if (op == "cdf") {
send_request(td_api::make_object<td_api::cancelDownloadFile>(as_file_id(args), false));

View File

@ -1900,6 +1900,13 @@ void FileManager::download(FileId file_id, std::shared_ptr<DownloadCallback> cal
node->set_download_limit(limit);
auto *file_info = get_file_id_info(file_id);
CHECK(new_priority == 0 || callback);
if (file_info->download_callback_ != nullptr && file_info->download_callback_.get() != callback.get()) {
// the callback will be destroyed soon and lost forever
// this would be an error and should never happen, unless we cancel previous download query
// in that case we send an error to the callback
CHECK(new_priority == 0);
file_info->download_callback_->on_download_error(file_id, Status::Error(200, "Cancelled"));
}
file_info->download_priority_ = narrow_cast<int8>(new_priority);
file_info->download_callback_ = std::move(callback);
// TODO: send current progress?

View File

@ -615,7 +615,7 @@ class CheckTestC : public Task {
if (text.substr(0, tag_.size()) == tag_) {
file_id_to_check_ = messageDocument->document_->document_->id_;
LOG(ERROR) << "GOT FILE " << to_string(messageDocument->document_->document_);
this->send_query(make_tl_object<td_api::downloadFile>(file_id_to_check_, 1, 0, 0),
this->send_query(make_tl_object<td_api::downloadFile>(file_id_to_check_, 1, 0, 0, false),
[](auto res) { check_td_error(res); });
}
}