From 2c1fbaf4c6eca8fb1902554290c6478e6bc2793b Mon Sep 17 00:00:00 2001 From: BrickMemer Date: Tue, 7 Apr 2026 10:40:19 +0200 Subject: [PATCH] Added CMakeLists.txt to File-Downloader --- CMakeLists.txt | 7 +++ LICENSE | 21 +++++++ Makefile | 23 ++++++++ README.md | 79 ++++++++++++++++++++++++++ common.h | 8 +++ console.cpp | 54 ++++++++++++++++++ console.h | 10 ++++ downloader.cpp | 149 +++++++++++++++++++++++++++++++++++++++++++++++++ downloader.h | 35 ++++++++++++ main.cpp | 13 +++++ 10 files changed, 399 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 common.h create mode 100644 console.cpp create mode 100644 console.h create mode 100644 downloader.cpp create mode 100644 downloader.h create mode 100644 main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c83edbd --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.16) + +add_library(FileDownloader SHARED downloader.cpp console.cpp) + +target_include_directories(FileDownloader + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..64dba5d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 ezBinary + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f0002af --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +# Makefile for FileDownloader project +# Works on macOS & linux + +CC = g++ +FLAGS = -Wall -std=c++11 +LIBS = -lcurl + +all: FileDownloader + +FileDownloader: main.o downloader.o console.o + $(CC) $(FLAGS) $^ -o $@ $(LIBS) + +main.o: main.cpp + $(CC) $(FLAGS) -c $< + +downloader.o: downloader.cpp + $(CC) $(FLAGS) -c $< + +console.o: console.cpp + $(CC) $(FLAGS) -c $< + +clean: + rm -f *.o FileDownloader diff --git a/README.md b/README.md new file mode 100644 index 0000000..7312238 --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# File Downloader +A simple File Downloader using C++ and libcURL. + +## Table of Contents +- [Introduction](#introduction) +- [Features](#features) +- [Compilation](#compilation) +- [Contributions](#contributions) +- [License](#license) +- [Feedback](#feedback) + +## Introduction +The File Downloader is a C++ project developed using the libcURL library. It facilitates downloading files from provided URLs using the cURL library and provides real-time download progress information, including download speed, percentage completion, and total download size. + +screenshot_filedownloader + +## Features +- Downloads files from provided URLs using cURL. +- Displays real-time download progress, including percentage completion and download speed. +- Provides a simple and easy to use command line interface for downloading files. + +## How to Use +1. **Input URL**: Provide the download link of the file you want to download. +2. **Start Download**: Press enter to initiate the download process. +3. **Monitor Progress**: Monitor the download progress, including the percentage completed and the download speed. +4. **Completion**: Once the download is complete, the file will be available for use. + +## Why File Downloader? +File Downloader was developed as a training project to learn more about the capabilities of the cURL and enhance understanding of network programming in C++. It aims to provide a practical example for those interested in integrating file downloading functionality into their projects. + +## Compilation + +To use File Downloader, simply clone the repository and compile the source code using your preferred C++ compiler. Ensure that you have the libcURL library installed on your system. + +### Compiling with Different Compilers + +File Downloader can be compiled using various compilers on different platforms. Below are instructions for compiling the project with commonly used compilers: + +#### 1. g++ + +```bash +g++ main.cpp downloader.cpp console.cpp -o FileDownloader -lcurl +``` + +#### 2. clang++ + +```bash +clang++ main.cpp downloader.cpp console.cpp -o FileDownloader -lcurl +``` + +#### 3. Microsoft Visual C++ (MSVC) + +```bash +cl /EHsc main.cpp downloader.cpp console.cpp /link /out:FileDownloader.exe +``` + +### Platform Compatibility + +This project's Makefile is designed to work seamlessly on both macOS and Linux systems. Below are instructions on how to use it : + +To compile the project on macOS and Linux systems, simply navigate to the project directory in the terminal and run the `make` command. This will compile the source files and generate the executable `FileDownloader`. + +--- + +Feel free to let me know if you need any further modifications! + +## Contributions +Contributions to File Downloader are welcome! Feel free to fork the repository, make improvements, and submit pull requests. + +## License +This project is licensed under the [MIT License](LICENSE). Feel free to use and modify it according to your needs. + +## Disclaimer +File Downloader is provided as-is, without any warranties or guarantees. I am not liable for any damages or issues arising from the use of this software. + +## Feedback +If you have any feedback, suggestions, or issues, please feel free to [open an issue](https://github.com/ezBinary/File-Downloader/issues) or [contact me](mailto:ez.like.binary@proton.me). I appreciate your input! + +Enjoy using File Downloader for your file downloading needs! diff --git a/common.h b/common.h new file mode 100644 index 0000000..f2b6e4e --- /dev/null +++ b/common.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/console.cpp b/console.cpp new file mode 100644 index 0000000..bb6313f --- /dev/null +++ b/console.cpp @@ -0,0 +1,54 @@ +#include "console.h" +#include +#include +// Check the validate URL format +bool isValidURL(const std::string& url) +{ + // Regular expression to validate URL format + std::regex urlRegex("(https?|ftp)://[^\\s/$.?#].[^\\s]*"); + return std::regex_match(url, urlRegex); +} + +// Get a valid URL from the user +std::string console::getValidURL() { + std::string url; + + while (true) + { + std::cout << "Enter the URL or 'q' to quit: "; + std::getline(std::cin, url); + + if (url == "q" || url == "Q") { + std::cout << "Input cancelled." << std::endl; + exit(0); + } + + if (isValidURL(url)) { + return url; + } + + std::cout << "Invalid URL format. Please enter a valid URL or 'q' to cancel." << std::endl; + } +} + +// Print Stylish Progress Bar +void console::drawStylishProgressBar(double progress, int width) { + int completedBlocks = static_cast(progress * width); + int remainingBlocks = width - completedBlocks; + int i; + // Clear previous output + std::cout << "\r" << std::flush; + + // Print new progress bar + std::cout << "["; + + for (i = 0; i < completedBlocks; ++i) { + std::cout << "\u2588"; // Full block Unicode character + } + + for (i = 0; i < remainingBlocks; ++i) { + std::cout << "\u2591"; // Light shade Unicode character + } + + std::cout << "] " << std::fixed << std::setprecision(0) << (progress * 100) << "%" << std::flush; + } diff --git a/console.h b/console.h new file mode 100644 index 0000000..e27920a --- /dev/null +++ b/console.h @@ -0,0 +1,10 @@ +#pragma once +#include "common.h" + +bool isValidURL(const std::string& url) ; + +namespace console +{ + std::string getValidURL(); + void drawStylishProgressBar(double progress, int width); +} \ No newline at end of file diff --git a/downloader.cpp b/downloader.cpp new file mode 100644 index 0000000..ea49745 --- /dev/null +++ b/downloader.cpp @@ -0,0 +1,149 @@ +#include "downloader.h" + +// Convert bytes to human readable format +std::string formatBytes(curl_off_t bytes) +{ + + static const char *suffixes[] = {"B", "KB", "MB", "GB", "TB"}; + int suffixIndex = 0; + double adjustedBytes = static_cast(bytes); + + while (adjustedBytes >= 1024 && suffixIndex < sizeof(suffixes) / sizeof(suffixes[0]) ) { + adjustedBytes /= 1024.0; + suffixIndex++; + } + + return std::to_string(adjustedBytes) + " " + suffixes[suffixIndex]; +} + +// Progress callback function +int FileDownloader::progressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) +{ + std::string SpeedStr; + DownloadProgress *progress = static_cast(clientp); + double percent; + double speed; + + // Update progress data + progress->bytesDownloaded = dlnow; + progress->totalSize = dltotal; + + // Calculate download completion percentage + percent = (dltotal > 0) ? (dlnow * 100.0 / dltotal) : 0.0; + + // Calculate download speed + auto elapsedTime = std::chrono::steady_clock::now() - progress->startTime; + speed = progress->bytesDownloaded / (std::chrono::duration_cast(elapsedTime).count() / 1000.0); // bytes per second + + // Clear previous progress bar + std::cout << "\r"; + + // Display progress bar + console::drawStylishProgressBar(percent / 100.0,50); + + // Display download size information + std::cout << " (" << formatBytes(progress->bytesDownloaded) << " of " << formatBytes(progress->totalSize) << ")"; + + // Display download speed + SpeedStr = " | Speed: " + formatBytes(speed) + "/s "; + std::cout << SpeedStr.c_str(); + + std::cout.flush(); // Ensure immediate output + + return 0; +} + +bool FileDownloader::Downloader() +{ + CURL *curl = curl_easy_init(); + DownloadProgress progress; + CURLcode res; + FILE *file; + + if (!curl) { + std::cerr << "Error initializing curl." << std::endl; + return false; + } + // Set the URL + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + + // Set progress callback function + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progress); + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progressCallback); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); // Enable progress data + + // Open file hanndle for writing + file = fopen(filename.c_str(), "wb"); + if (!file) { + std::cerr << "Error opening file for writing" << std::endl; + curl_easy_cleanup(curl); + return false; + } + + // Set file as the target for the download + curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); + + // Record start time + progress.startTime = std::chrono::steady_clock::now(); + + // Perform the request + res = curl_easy_perform(curl); + + // Check for errors + if (res != CURLE_OK) + { + std::cerr << "\ncurl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl; + return false; + } else + { + std::cout << "\nDownload completed successfully!" << std::endl; + } + + + return true; +} + +FileDownloader::FileDownloader(const std::string& i_url) : url(i_url), curl(nullptr), file(nullptr) +{ + // Initialize libcurl + if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) + { + std::cerr << "Error initializing libcurl." << std::endl; + } + + std::size_t found = url.find_last_of("/"); + if (found != std::string::npos) + { + filename = url.substr(found + 1); + } + else + { + // invalid URL without filename + std::cerr << "Error: Invalid URL format." << std::endl; + } + +} + +FileDownloader::~FileDownloader() +{ + // close curl handle + curl_easy_cleanup(curl); + + // close file handle + + fclose(file); + + // If the download process was unsuccessful, simply delete the corrupted downloaded file. + if(!check) + { + std::remove(filename.c_str()); + } + + // close global init + curl_global_cleanup(); +} + +void FileDownloader::download() +{ + check = FileDownloader::Downloader(); +} diff --git a/downloader.h b/downloader.h new file mode 100644 index 0000000..87fffb3 --- /dev/null +++ b/downloader.h @@ -0,0 +1,35 @@ +#pragma once + +#include "console.h" + + +class FileDownloader { +private: +// Struct to encapsulate download progress data + struct DownloadProgress { + curl_off_t bytesDownloaded; + curl_off_t totalSize; + std::chrono::steady_clock::time_point startTime; + + DownloadProgress() : bytesDownloaded(0), totalSize(0), startTime(std::chrono::steady_clock::now()) {} +}; + + + std::string url; + std::string filename; + CURL* curl; + FILE* file; + + bool check; + bool Downloader(); + static int progressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); + +public: + + + + FileDownloader(const std::string& xurl); + ~FileDownloader(); + + void download(); +}; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..ea9f88e --- /dev/null +++ b/main.cpp @@ -0,0 +1,13 @@ +#include "downloader.h" + +int main() +{ + // Get a valid URL from the user + std::string i_url = console::getValidURL(); + + // Download + FileDownloader fdl(i_url.c_str()); + fdl.download(); + + return 0; +}