Get filenames of downloaded files

This commit is contained in:
Yousef Khadadeh 2025-10-11 13:02:07 +03:00
parent 1175424c23
commit 48156cb4bf
5 changed files with 77 additions and 6 deletions

View file

@ -1,7 +1,7 @@
#ifndef __EMSCRIPTEN__
#include "FileDownloader.hpp"
#ifndef __EMSCRIPTEN__
#include <curl/multi.h>
#include <iostream>
#include <curl/curl.h>
@ -18,18 +18,23 @@ void FileDownloader::init() {
shutdownThread = false;
}
std::shared_ptr<FileDownloader::DownloadData> FileDownloader::download_data_from_url(std::string_view url) {
std::shared_ptr<FileDownloader::DownloadData> FileDownloader::download_data_from_url(const std::string& url) {
DownloadHandler h;
auto downloadData = std::make_shared<DownloadData>();
h.data = downloadData;
h.eHandle = curl_easy_init();
downloadData->fileName = get_potential_filename_from_url(url);
curl_easy_setopt(h.eHandle, CURLOPT_URL, url.data());
curl_easy_setopt(h.eHandle, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(h.eHandle, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_ALL);
curl_easy_setopt(h.eHandle, CURLOPT_USERAGENT, "InfiniPaint/1.0");
curl_easy_setopt(h.eHandle, CURLOPT_USERAGENT, "InfiniPaint/0.2.0");
curl_easy_setopt(h.eHandle, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(h.eHandle, CURLOPT_XFERINFODATA, h.data.get());
curl_easy_setopt(h.eHandle, CURLOPT_XFERINFOFUNCTION, download_progress_callback);
curl_easy_setopt(h.eHandle, CURLOPT_HEADERFUNCTION, header_callback);
curl_easy_setopt(h.eHandle, CURLOPT_HEADERDATA, h.data.get());
disable_ssl_verification(h.eHandle);
save_to_string(h.eHandle, &h.data->str);
@ -131,6 +136,16 @@ int FileDownloader::download_progress_callback(void *clientp, curl_off_t dltotal
return 0;
}
size_t FileDownloader::header_callback(char* buffer, size_t size, size_t nitems, void* userData) {
DownloadData* downData = static_cast<DownloadData*>(userData);
std::string header(buffer, nitems);
std::string newFilename = parse_http_headers_for_filename(header);
if(!newFilename.empty())
downData->fileName = newFilename;
return nitems;
}
void FileDownloader::cleanup() {
if(downloadThread && downloadThread->joinable()) {
shutdownThread = true;
@ -142,3 +157,35 @@ void FileDownloader::cleanup() {
}
#endif
std::string FileDownloader::parse_http_headers_for_filename(std::string header) {
std::string headerLower = header;
std::transform(headerLower.begin(), headerLower.end(), headerLower.begin(), ::tolower);
std::string contentDispositionHeader = "content-disposition:";
size_t contentDispositionLoc = headerLower.find(contentDispositionHeader);
if(contentDispositionLoc < headerLower.size()) {
size_t newlineHeaderLoc = std::min<size_t>(headerLower.find('\n', contentDispositionLoc), headerLower.size());
std::string filenameHeader = "filename=";
size_t filenameLoc = headerLower.find(filenameHeader, contentDispositionLoc);
if(filenameLoc < newlineHeaderLoc) {
header = header.substr(filenameLoc + filenameHeader.size());
header = header.substr(0, header.find(';'));
if(header.size() >= 2 && header.front() == '"' && header.back() == '"') {
header.erase(0, 1);
header.pop_back();
}
return header;
}
}
return "";
}
std::string FileDownloader::get_potential_filename_from_url(const std::string& url) {
size_t fileNameStart = url.find_last_of('/');
if(fileNameStart >= (url.size() - 1))
return "";
std::string fileName = url.substr(fileNameStart + 1);
return fileName.substr(0, fileName.find('?'));
}

View file

@ -20,12 +20,13 @@ class FileDownloader {
FAILURE
};
std::string str;
std::string fileName;
std::atomic<float> progress = 0;
std::atomic<Status> status = DownloadData::Status::IN_PROGRESS;
};
static void init();
static std::shared_ptr<DownloadData> download_data_from_url(std::string_view url);
static std::shared_ptr<DownloadData> download_data_from_url(const std::string& url);
static void cleanup();
private:
@ -38,6 +39,9 @@ class FileDownloader {
static std::vector<DownloadHandler> currentDownloads;
static std::string get_potential_filename_from_url(const std::string& url);
static std::string parse_http_headers_for_filename(std::string header);
#ifndef __EMSCRIPTEN__
static std::mutex newDownloadsMutex;
static std::vector<DownloadHandler> newDownloads;
@ -47,6 +51,7 @@ class FileDownloader {
static void save_to_string(CURL* curl, std::string* str);
static int download_progress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
static size_t header_callback(char* buffer, size_t size, size_t nitems, void* userData);
static void disable_ssl_verification(CURL* curl);
static size_t write_to_string(void* contents, size_t size, size_t nmemb, void* userp);
static void add_new_handlers_to_multi();

View file

@ -16,10 +16,24 @@ void FileDownloader::download_progress(emscripten_fetch_t *fetch) {
void FileDownloader::download_succeeded(emscripten_fetch_t *fetch) {
DownloadData* downData = static_cast<DownloadData*>(fetch->userData);
downData->str = std::string(fetch->data, fetch->numBytes);
size_t headerLength = emscripten_fetch_get_response_headers_length(fetch);
if(headerLength != 0) {
std::string header(headerLength + 1, ' ');
emscripten_fetch_get_response_headers(fetch, header.data(), header.size() + 1);
if(!header.empty())
header.pop_back(); // Remove additional null terminator
std::string newFilename = parse_http_headers_for_filename(header);
if(!newFilename.empty())
downData->fileName = newFilename;
}
downData->status = DownloadData::Status::SUCCESS;
std::erase_if(currentDownloads, [fetch](DownloadHandler& d) {
return (DownloadData*)fetch->userData == d.data.get();
});
emscripten_fetch_close(fetch);
}
@ -35,9 +49,10 @@ void FileDownloader::download_failed(emscripten_fetch_t *fetch) {
void FileDownloader::init() {
}
std::shared_ptr<FileDownloader::DownloadData> FileDownloader::download_data_from_url(std::string_view url) {
std::shared_ptr<FileDownloader::DownloadData> FileDownloader::download_data_from_url(const std::string& url) {
DownloadHandler& newDownload = currentDownloads.emplace_back();
newDownload.data = std::make_shared<DownloadData>();
newDownload.data->fileName = get_potential_filename_from_url(url);
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);

View file

@ -462,6 +462,7 @@ void DrawingProgram::update_downloading_dropped_files() {
ResourceData newResource;
newResource.data = std::make_shared<std::string>(downFile.downData->str);
newResource.name = downFile.downData->fileName;
ServerClientID imageID = world.rMan.add_resource(newResource);
ResourceDisplay* display = world.rMan.get_display_data(imageID);
if(display->get_type() == ResourceDisplay::Type::FILE) {

View file

@ -7,6 +7,7 @@
#include "../ResourceManager.hpp"
#include "DrawingProgramEditToolBase.hpp"
#include <fstream>
#ifdef __EMSCRIPTEN__
#include <EmscriptenHelpers/emscripten_browser_file.h>
#endif
@ -31,6 +32,8 @@ bool ImageEditTool::edit_gui(const std::shared_ptr<DrawComponent>& comp) {
t.gui.push_id("edit tool image");
t.gui.text_label_centered("File Properties");
std::optional<ResourceData> resourceData = drawP.world.rMan.get_resource(a->d.imageID);
if(resourceData) {
t.gui.text_label("Name: " + resourceData->name);
if(t.gui.text_button_wide("file download", "Download file")) {