Added CMakeLists.txt to File-Downloader
This commit is contained in:
7
CMakeLists.txt
Normal file
7
CMakeLists.txt
Normal file
@@ -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})
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -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.
|
||||||
23
Makefile
Normal file
23
Makefile
Normal file
@@ -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
|
||||||
79
README.md
Normal file
79
README.md
Normal file
@@ -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.
|
||||||
|
|
||||||
|
<img width="1061" alt="screenshot_filedownloader" src="https://github.com/ezBinary/File-Downloader/assets/164413461/3a934709-5c81-4125-ba4a-a9a2396e7f6c">
|
||||||
|
|
||||||
|
## 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!
|
||||||
8
common.h
Normal file
8
common.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <iomanip>
|
||||||
54
console.cpp
Normal file
54
console.cpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#include "console.h"
|
||||||
|
#include <string>
|
||||||
|
#include <regex>
|
||||||
|
// 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<int>(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;
|
||||||
|
}
|
||||||
10
console.h
Normal file
10
console.h
Normal file
@@ -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);
|
||||||
|
}
|
||||||
149
downloader.cpp
Normal file
149
downloader.cpp
Normal file
@@ -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<double>(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<DownloadProgress*>(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<std::chrono::milliseconds>(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();
|
||||||
|
}
|
||||||
35
downloader.h
Normal file
35
downloader.h
Normal file
@@ -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();
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user