Compare commits

...
Author SHA1 Message Date
FredApps 4fe6429431 api_clients: use tokio::fs inside async functions
Resolves SonarCloud rust:S7493 (blocking file operation in async code) in artcraft_client, artcraft_router, midjourney_client and openai_sora_client.
2026-11-10 12:04:01 +01:00
10 changed files with 13 additions and 24 deletions
@@ -33,6 +33,7 @@ chrono.workspace = true
cookie.workspace = true
log.workspace = true
reqwest = { workspace = true, features = ["multipart", "gzip"] }
tokio = { workspace = true, features = ["fs"] }
url.workspace = true
# Serialization
@@ -60,7 +60,7 @@ pub async fn legacy_upload_media_file_from_file<P: AsRef<Path>>(
.build()
.map_err(|err| StorytellerError::Client(ClientError::from(err)))?;
let file_bytes = std::fs::read(args.path.as_ref())
let file_bytes = tokio::fs::read(args.path.as_ref()).await
.map_err(|err| StorytellerError::Client(ClientError::from(err)))?;
let file_name = args.path.as_ref().file_name()
.and_then(|n| n.to_str()).unwrap_or("file").to_string();
@@ -69,7 +69,7 @@ pub async fn upload_image_media_file_from_file_with_idempotency_token<P: AsRef<P
.build()
.map_err(|err| StorytellerError::Client(ClientError::from(err)))?;
let file_bytes = std::fs::read(args.path.as_ref())
let file_bytes = tokio::fs::read(args.path.as_ref()).await
.map_err(|err| StorytellerError::Client(ClientError::from(err)))?;
let file_name = args.path.as_ref().file_name()
.and_then(|n| n.to_str()).unwrap_or("file").to_string();
@@ -35,7 +35,7 @@ pub async fn upload_new_engine_asset_from_file<P: AsRef<Path>>(
let client = storyteller_client_builder(api_host)
.build()?;
let file_bytes = std::fs::read(path.as_ref())?;
let file_bytes = tokio::fs::read(path.as_ref()).await?;
let file_name = path.as_ref().file_name()
.and_then(|n| n.to_str()).unwrap_or("file").to_string();
let mut form = Form::new()
@@ -61,7 +61,7 @@ pub async fn upload_video_media_file_from_file<P: AsRef<Path>>(
let client = storyteller_client_builder(args.api_host)
.build()?;
let file_bytes = std::fs::read(args.path.as_ref())?;
let file_bytes = tokio::fs::read(args.path.as_ref()).await?;
let file_name = args.path.as_ref().file_name()
.and_then(|n| n.to_str()).unwrap_or("file").to_string();
let mut form = Form::new()
@@ -1,5 +1,3 @@
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use log::{info, warn};
@@ -68,13 +66,7 @@ pub async fn download_media_file<P: AsRef<Path>>(
let bytes = download_bytes(cdn_url).await?;
// 4. Write to disk.
let mut file = fs::File::create(&output_path)
.map_err(|err| StorytellerError::Client(ClientError::IoError(err)))?;
file.write_all(&bytes)
.map_err(|err| StorytellerError::Client(ClientError::IoError(err)))?;
file.flush()
tokio::fs::write(&output_path, &bytes).await
.map_err(|err| StorytellerError::Client(ClientError::IoError(err)))?;
info!("Downloaded {} bytes to {:?}", bytes.len(), output_path);
@@ -31,7 +31,7 @@ reqwest.workspace = true
serde.workspace = true
wreq.workspace = true
serde_derive.workspace = true
tokio.workspace = true
tokio = { workspace = true, features = ["fs"] }
uuid.workspace = true
[dev-dependencies]
@@ -24,7 +24,7 @@ pub(crate) async fn upload_to_kinovi_web(
.unwrap_or_else(|| "jpg".to_string());
let file_bytes = match maybe_local_path {
Some(local_path) => match std::fs::read(local_path) {
Some(local_path) => match tokio::fs::read(local_path).await {
Ok(bytes) => {
info!("Reusing predownloaded file for kinovi upload: {}", source_url);
bytes
@@ -93,7 +93,7 @@ impl MidjourneyCredentialManager {
let _guard = self.mutation.lock().await;
// Remove every format, so a later startup cannot migrate a logged-out account.
for path in self.legacy_paths.iter().chain([&self.credential_path]) {
match std::fs::remove_file(path) {
match tokio::fs::remove_file(path).await {
Ok(()) => {},
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {},
Err(err) => return Err(err.into()),
@@ -11,7 +11,7 @@ use errors::AnyhowResult;
use log::{debug, error, warn};
use serde_derive::{Deserialize, Serialize};
use std::fmt::{Debug, Display};
use std::{fs::File, io::Write, path::Path};
use std::path::Path;
use url::Url;
use wreq::Client;
@@ -271,9 +271,7 @@ pub async fn get_image_gen_status(status_request: &StatusRequest, credentials: &
pub async fn save_generations_to_dir(generations: &[Generation], dir: &str) -> AnyhowResult<()> {
let dir = Path::new(dir);
if !dir.exists() {
std::fs::create_dir_all(dir)?;
}
tokio::fs::create_dir_all(dir).await?;
for generation in generations {
let response = wreq::get(generation.url.clone()).send().await?;
let image_bytes = response.bytes().await?;
@@ -282,15 +280,13 @@ pub async fn save_generations_to_dir(generations: &[Generation], dir: &str) -> A
let ext = url.path().split(".").last().unwrap_or("png");
let path = Path::new(dir).join(format!("{}.{}", generation.id, ext));
let mut file = File::create(path)?;
file.write_all(&image_bytes)?;
tokio::fs::write(path, &image_bytes).await?;
let thumbnail_url = generation.encodings.thumbnail.path.clone();
let thumbnail_response = wreq::get(thumbnail_url).send().await?;
let thumbnail_bytes = thumbnail_response.bytes().await?;
let thumbnail_path = Path::new(dir).join(format!("{}_thumbnail.{}", generation.id, ext));
let mut thumbnail_file = File::create(thumbnail_path)?;
thumbnail_file.write_all(&thumbnail_bytes)?;
tokio::fs::write(thumbnail_path, &thumbnail_bytes).await?;
}
Ok(())