commit 2c1fbaf4c6eca8fb1902554290c6478e6bc2793b Author: BrickMemer Date: Tue Apr 7 10:40:19 2026 +0200 Added CMakeLists.txt to File-Downloader 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; +}