Cargo.toml
사용할 모듈 정보를 입력한다.
tokio: 파일, async
reqwest : http
main을 async로 동작시키려면 tokio feature중 "rt" or "rt-multi-thread", "macros" 필요
https 사용을 위해 reqwest feature 중 "rustls-tls" or "native-tls" 필요
[package]
name = "sample"
version = "0.1.0"
edition = "2021"
[features]
default = []
[dependencies]
tokio = { version = "1", features = [ "fs", "rt-multi-thread", "macros"] }
reqwest = { version = "0.12", features = ["rustls-tls"] }
에러 열거형 : main.rs
여러가지 모듈의 에러를 하나로 관리하기 위해 다운로드 패키지에서 사용할 Error를 작성해 준다.
각 에러타입에 따라 From을 작성해 주면 해당 에러 발생시 이 패키지의 에러로 변경이 가능해진다.
보통 에러는 매번 핸들링하지 않고, '?'로 돌려보내게 되는데, 이때 From을 통해 변환이 일어나게 된다.
use std::error::Error as StdError;
use std::fmt;
#[derive(Debug)]
pub enum Error {
Io(std::io::Error),
Fetcher(String),
Timeout,
Http(reqwest::Error),
NotFound,
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::Io(err)
}
}
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Self {
Error::Http(err)
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Error::Io(err) => Some(err),
Error::Fetcher(_) => None,
Error::Timeout => None,
Error::Http(err) => Some(err),
Error::NotFound => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Io(err) => write!(f, "io error: {}", err),
Error::Fetcher(msg) => write!(f, "fetcher error: {}", msg),
Error::Timeout => write!(f, "timeout"),
Error::Http(err) => write!(f, "http error: {}", err),
Error::NotFound => write!(f, "not found"),
}
}
}
다운로드 객체 : downloader.rs
reqwest::Client 만 하나 포함하고 있는 다운로드를 수행할 객체를 작성한다.
use std::path::{Path, PathBuf};
use crate::Error;
use tokio::{
fs, // fs::File
io::AsyncWriteExt , // write_all
};
#[derive(Debug)]
struct HttpFileDownloader {
client: reqwest::Client,
}
impl Default for HttpFileDownloader {
fn default() -> Self {
Self {
client: Default::default(),
}
}
}
다운로드 함수 : downloader.rs
파일을 저장할 path 를 전달받아 각 폴더를 만들고, 파일을 생성한다.
http/https로 파일을 다운로드 받아 해당 파일에 추가한다.
impl HttpFileDownloader {
pub fn new() -> Self {
Self {
client: Default::default(),
}
}
// destination : 다운로드 받을 폴더 위치
// url : 다운로드할 파일 url 문자열
pub async fn download(&self, destination: impl AsRef<Path>, url: &str) -> Result<PathBuf, Error> {
// 파일 저장할 폴더 path 얻기
let destination = destination.as_ref();
// 파일 저장할 폴더 생성
if !destination.exists() {
fs::create_dir_all(destination).await?
}
// path를 파일명을 포함한 full file path로 변경
let path = if destination.is_file() {
destination.to_owned()
} else {
destination.join("1.79.0.zip")
};
// 파일 생성 및 열기
let mut file = fs::File::create(&path).await?;
// https 파일 요청
let mut response = self
.client
.get(url)
.send()
.await?
.error_for_status()?;
// 청크 단위로 파일다운로드 및 파일에 저장
while let Some(chunk) = response.chunk().await? {
file.write_all(&chunk).await?;
}
Ok(path)
}
}
메인함수 : main.rs
pub mod downloader;
pub use crate::downloader::HttpFileDownloader;
#[tokio::main]
async fn main() -> Result<(), Box<dyn StdError>> {
let _ = HttpFileDownloader::default().download(
".",
"https://github.com/rust-lang/rust/archive/refs/tags/1.79.0.zip"
).await?;
Ok(())
}
'프로그래밍 > RUST' 카테고리의 다른 글
[RUST] 인자 입력 받아 명령 프로세스 실행하기 (0) | 2024.06.24 |
---|---|
[RUST] 다른 언어와 다른 부분, 특징 정리 중.... (0) | 2024.06.18 |
[RUST] cargo 기본 사용법 (0) | 2024.05.14 |