Skip to content

Commit 87cc585

Browse files
VirtualPirateahkohdFabianLars
authored
feat(upload): Add transfer_speed for downloading and uploading files (#1797)
Co-authored-by: Victor Aremu <[email protected]> Co-authored-by: Fabian-Lars <[email protected]>
1 parent e0d2e2c commit 87cc585

File tree

4 files changed

+74
-1
lines changed

4 files changed

+74
-1
lines changed

.changes/add-transfer-speed.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"upload": "minor"
3+
"upload-js": "minor"
4+
---
5+
6+
Added feature for calculating `transfer_speed` during file uploads and downloads

plugins/upload/guest-js/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { invoke, Channel } from '@tauri-apps/api/core'
77
interface ProgressPayload {
88
progress: number
99
total: number
10+
transferSpeed: number
1011
}
1112

1213
type ProgressHandler = (progress: ProgressPayload) => void

plugins/upload/src/lib.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
1414
)]
1515

16+
mod transfer_stats;
17+
use transfer_stats::TransferStats;
18+
1619
use futures_util::TryStreamExt;
1720
use serde::{ser::Serializer, Serialize};
1821
use tauri::{
@@ -55,9 +58,11 @@ impl Serialize for Error {
5558
}
5659

5760
#[derive(Clone, Serialize)]
61+
#[serde(rename_all = "camelCase")]
5862
struct ProgressPayload {
5963
progress: u64,
6064
total: u64,
65+
transfer_speed: u64,
6166
}
6267

6368
#[command]
@@ -88,11 +93,14 @@ async fn download(
8893
let mut file = BufWriter::new(File::create(file_path).await?);
8994
let mut stream = response.bytes_stream();
9095

96+
let mut stats = TransferStats::default();
9197
while let Some(chunk) = stream.try_next().await? {
9298
file.write_all(&chunk).await?;
99+
stats.record_chunk_transfer(chunk.len());
93100
let _ = on_progress.send(ProgressPayload {
94101
progress: chunk.len() as u64,
95102
total,
103+
transfer_speed: stats.transfer_speed,
96104
});
97105
}
98106
file.flush().await?;
@@ -138,10 +146,16 @@ async fn upload(
138146
fn file_to_body(channel: Channel<ProgressPayload>, file: File) -> reqwest::Body {
139147
let stream = FramedRead::new(file, BytesCodec::new()).map_ok(|r| r.freeze());
140148

149+
let mut stats = TransferStats::default();
141150
reqwest::Body::wrap_stream(ReadProgressStream::new(
142151
stream,
143152
Box::new(move |progress, total| {
144-
let _ = channel.send(ProgressPayload { progress, total });
153+
stats.record_chunk_transfer(progress as usize);
154+
let _ = channel.send(ProgressPayload {
155+
progress,
156+
total,
157+
transfer_speed: stats.transfer_speed,
158+
});
145159
}),
146160
))
147161
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use std::time::Instant;
6+
7+
// The TransferStats struct is used to track and calculate the transfer speed of data chunks over time.
8+
pub struct TransferStats {
9+
accumulated_chunk_len: usize, // Total length of chunks transferred in the current period
10+
accumulated_time: u128, // Total time taken for the transfers in the current period
11+
pub transfer_speed: u64, // Calculated transfer speed in bytes per second
12+
start_time: Instant, // Time when the current period started
13+
granularity: u32, // Time period (in milliseconds) over which the transfer speed is calculated
14+
}
15+
16+
impl TransferStats {
17+
// Initializes a new TransferStats instance with the specified granularity.
18+
pub fn start(granularity: u32) -> Self {
19+
Self {
20+
accumulated_chunk_len: 0,
21+
accumulated_time: 0,
22+
transfer_speed: 0,
23+
start_time: Instant::now(),
24+
granularity,
25+
}
26+
}
27+
// Records the transfer of a data chunk and updates the transfer speed if the granularity period has elapsed.
28+
pub fn record_chunk_transfer(&mut self, chunk_len: usize) {
29+
let now = Instant::now();
30+
let it_took = now.duration_since(self.start_time).as_millis();
31+
self.accumulated_chunk_len += chunk_len;
32+
self.accumulated_time += it_took;
33+
34+
// If the accumulated time exceeds the granularity, calculate the transfer speed.
35+
if self.accumulated_time >= self.granularity as u128 {
36+
self.transfer_speed =
37+
(self.accumulated_chunk_len as u128 / self.accumulated_time * 1024) as u64;
38+
self.accumulated_chunk_len = 0;
39+
self.accumulated_time = 0;
40+
}
41+
42+
// Reset the start time for the next period.
43+
self.start_time = now;
44+
}
45+
}
46+
47+
// Provides a default implementation for TransferStats with a granularity of 500 milliseconds.
48+
impl Default for TransferStats {
49+
fn default() -> Self {
50+
Self::start(500) // Default granularity is 500
51+
}
52+
}

0 commit comments

Comments
 (0)