From 87cc58527d769960427a2f46bb10532f5dcf7ace Mon Sep 17 00:00:00 2001 From: Artaza Sameen <39516575+VirtualPirate@users.noreply.github.com> Date: Tue, 5 Nov 2024 03:11:48 +0530 Subject: [PATCH] feat(upload): Add transfer_speed for downloading and uploading files (#1797) Co-authored-by: Victor Aremu Co-authored-by: Fabian-Lars --- .changes/add-transfer-speed.md | 6 ++++ plugins/upload/guest-js/index.ts | 1 + plugins/upload/src/lib.rs | 16 ++++++++- plugins/upload/src/transfer_stats.rs | 52 ++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 .changes/add-transfer-speed.md create mode 100644 plugins/upload/src/transfer_stats.rs diff --git a/.changes/add-transfer-speed.md b/.changes/add-transfer-speed.md new file mode 100644 index 00000000..bd5965cd --- /dev/null +++ b/.changes/add-transfer-speed.md @@ -0,0 +1,6 @@ +--- +"upload": "minor" +"upload-js": "minor" +--- + +Added feature for calculating `transfer_speed` during file uploads and downloads \ No newline at end of file diff --git a/plugins/upload/guest-js/index.ts b/plugins/upload/guest-js/index.ts index 9586a241..7c5bf796 100644 --- a/plugins/upload/guest-js/index.ts +++ b/plugins/upload/guest-js/index.ts @@ -7,6 +7,7 @@ import { invoke, Channel } from '@tauri-apps/api/core' interface ProgressPayload { progress: number total: number + transferSpeed: number } type ProgressHandler = (progress: ProgressPayload) => void diff --git a/plugins/upload/src/lib.rs b/plugins/upload/src/lib.rs index 9351b246..6a399fd1 100644 --- a/plugins/upload/src/lib.rs +++ b/plugins/upload/src/lib.rs @@ -13,6 +13,9 @@ html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png" )] +mod transfer_stats; +use transfer_stats::TransferStats; + use futures_util::TryStreamExt; use serde::{ser::Serializer, Serialize}; use tauri::{ @@ -55,9 +58,11 @@ impl Serialize for Error { } #[derive(Clone, Serialize)] +#[serde(rename_all = "camelCase")] struct ProgressPayload { progress: u64, total: u64, + transfer_speed: u64, } #[command] @@ -88,11 +93,14 @@ async fn download( let mut file = BufWriter::new(File::create(file_path).await?); let mut stream = response.bytes_stream(); + let mut stats = TransferStats::default(); while let Some(chunk) = stream.try_next().await? { file.write_all(&chunk).await?; + stats.record_chunk_transfer(chunk.len()); let _ = on_progress.send(ProgressPayload { progress: chunk.len() as u64, total, + transfer_speed: stats.transfer_speed, }); } file.flush().await?; @@ -138,10 +146,16 @@ async fn upload( fn file_to_body(channel: Channel, file: File) -> reqwest::Body { let stream = FramedRead::new(file, BytesCodec::new()).map_ok(|r| r.freeze()); + let mut stats = TransferStats::default(); reqwest::Body::wrap_stream(ReadProgressStream::new( stream, Box::new(move |progress, total| { - let _ = channel.send(ProgressPayload { progress, total }); + stats.record_chunk_transfer(progress as usize); + let _ = channel.send(ProgressPayload { + progress, + total, + transfer_speed: stats.transfer_speed, + }); }), )) } diff --git a/plugins/upload/src/transfer_stats.rs b/plugins/upload/src/transfer_stats.rs new file mode 100644 index 00000000..0ae32ec8 --- /dev/null +++ b/plugins/upload/src/transfer_stats.rs @@ -0,0 +1,52 @@ +// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use std::time::Instant; + +// The TransferStats struct is used to track and calculate the transfer speed of data chunks over time. +pub struct TransferStats { + accumulated_chunk_len: usize, // Total length of chunks transferred in the current period + accumulated_time: u128, // Total time taken for the transfers in the current period + pub transfer_speed: u64, // Calculated transfer speed in bytes per second + start_time: Instant, // Time when the current period started + granularity: u32, // Time period (in milliseconds) over which the transfer speed is calculated +} + +impl TransferStats { + // Initializes a new TransferStats instance with the specified granularity. + pub fn start(granularity: u32) -> Self { + Self { + accumulated_chunk_len: 0, + accumulated_time: 0, + transfer_speed: 0, + start_time: Instant::now(), + granularity, + } + } + // Records the transfer of a data chunk and updates the transfer speed if the granularity period has elapsed. + pub fn record_chunk_transfer(&mut self, chunk_len: usize) { + let now = Instant::now(); + let it_took = now.duration_since(self.start_time).as_millis(); + self.accumulated_chunk_len += chunk_len; + self.accumulated_time += it_took; + + // If the accumulated time exceeds the granularity, calculate the transfer speed. + if self.accumulated_time >= self.granularity as u128 { + self.transfer_speed = + (self.accumulated_chunk_len as u128 / self.accumulated_time * 1024) as u64; + self.accumulated_chunk_len = 0; + self.accumulated_time = 0; + } + + // Reset the start time for the next period. + self.start_time = now; + } +} + +// Provides a default implementation for TransferStats with a granularity of 500 milliseconds. +impl Default for TransferStats { + fn default() -> Self { + Self::start(500) // Default granularity is 500 + } +}