feat(upload): Add relevant headers and emit file size

Co-authored-by: yonatan.bendahan@gmail.com <yonatan.bendahan@gmail.com>
pull/39/head
FabianLars 3 years ago
parent 27b73413e9
commit 2c439f5a42
No known key found for this signature in database
GPG Key ID: 3B12BC1DEBF61125

1
Cargo.lock generated

@ -4261,6 +4261,7 @@ dependencies = [
"thiserror", "thiserror",
"tokio", "tokio",
"tokio-util", "tokio-util",
"url",
] ]
[[package]] [[package]]

@ -13,8 +13,9 @@ serde_json.workspace = true
tauri.workspace = true tauri.workspace = true
log.workspace = true log.workspace = true
thiserror.workspace = true thiserror.workspace = true
tokio = { version = "1.17", features = [ "fs" ] } tokio = { version = "1", features = [ "fs" ] }
tokio-util = { version = "0.7", features = [ "codec" ] } tokio-util = { version = "0.7", features = [ "codec" ] }
reqwest = { version = "0.11", features = [ "json", "stream" ] } reqwest = { version = "0.11", features = [ "json", "stream" ] }
futures = "0.3" futures = "0.3"
read-progress-stream = "1.0.0" read-progress-stream = "1"
url = "2"

@ -1,3 +1,18 @@
type ProgressHandler = (progress: number, total: number) => void; interface BaseHandlerData {
export default function upload(url: string, filePath: string, progressHandler?: ProgressHandler, headers?: Map<string, string>): Promise<void>; id: number;
export {}; }
interface ProgressHandlerData extends BaseHandlerData {
progress: number;
total: number;
}
interface SizeHandlerData extends BaseHandlerData {
size: number;
}
interface ResponseData {
text: string;
status: number;
}
type ProgressHandler = (data: ProgressHandlerData) => unknown;
type SizeHandler = (data: SizeHandlerData) => unknown;
export default function upload(url: string, filePath: string, progressHandler?: ProgressHandler, fileSizeHandler?: SizeHandler, headers?: Record<string, string>): Promise<ResponseData>;
export {};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,37 +1,46 @@
import { invoke } from '@tauri-apps/api/tauri'; import { invoke } from '@tauri-apps/api/tauri';
import { appWindow } from '@tauri-apps/api/window'; import { appWindow } from '@tauri-apps/api/window';
const handlers = new Map(); var UploadEvent;
let listening = false; (function (UploadEvent) {
async function listenToUploadEventIfNeeded() { UploadEvent["progress"] = "upload://progress";
if (listening) { UploadEvent["fileSize"] = "upload://file-size";
return await Promise.resolve(); })(UploadEvent || (UploadEvent = {}));
} const handlers = new Map();
return await appWindow const listeningMap = new Map();
.listen("upload://progress", ({ payload }) => { const getIdForEvent = (event, id) => `${event}-${id}`;
const handler = handlers.get(payload.id); async function listenToEventIfNeeded(event) {
if (handler != null) { if (listeningMap.get(event)) {
handler(payload.progress, payload.total); return;
} }
}) return appWindow.listen(event, ({ payload }) => {
.then(() => { const eventId = getIdForEvent(event, payload.id);
listening = true; const handler = handlers.get(eventId);
}); if (typeof handler === 'function') {
} handler(payload);
async function upload(url, filePath, progressHandler, headers) { }
const ids = new Uint32Array(1); });
window.crypto.getRandomValues(ids); }
const id = ids[0]; async function upload(url, filePath, progressHandler, fileSizeHandler, headers) {
if (progressHandler != null) { const ids = new Uint32Array(1);
handlers.set(id, progressHandler); window.crypto.getRandomValues(ids);
} const id = ids[0];
await listenToUploadEventIfNeeded(); if (progressHandler) {
await invoke("plugin:upload|upload", { const eventId = getIdForEvent(UploadEvent.progress, id);
id, handlers.set(eventId, progressHandler);
url, }
filePath, if (fileSizeHandler) {
headers: headers !== null && headers !== void 0 ? headers : {}, const eventId = getIdForEvent(UploadEvent.fileSize, id);
}); handlers.set(eventId, fileSizeHandler);
}
await listenToEventIfNeeded(UploadEvent.progress);
await listenToEventIfNeeded(UploadEvent.fileSize);
return await invoke('plugin:upload|upload', {
id,
url,
filePath,
headers: headers !== null && headers !== void 0 ? headers : {},
});
} }
export { upload as default }; export { upload as default };

@ -1 +1 @@
{"version":3,"file":"index.mjs","sources":["../index.ts"],"sourcesContent":[null],"names":[],"mappings":";;;AAUA,MAAM,QAAQ,GAAiC,IAAI,GAAG,EAAE,CAAC;AACzD,IAAI,SAAS,GAAG,KAAK,CAAC;AAEtB,eAAe,2BAA2B,GAAA;AACxC,IAAA,IAAI,SAAS,EAAE;AACb,QAAA,OAAO,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;AAChC,KAAA;AACD,IAAA,OAAO,MAAM,SAAS;SACnB,MAAM,CAAkB,mBAAmB,EAAE,CAAC,EAAE,OAAO,EAAE,KAAI;QAC5D,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,OAAO,IAAI,IAAI,EAAE;YACnB,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;AAC1C,SAAA;AACH,KAAC,CAAC;SACD,IAAI,CAAC,MAAK;QACT,SAAS,GAAG,IAAI,CAAC;AACnB,KAAC,CAAC,CAAC;AACP,CAAC;AAEc,eAAe,MAAM,CAClC,GAAW,EACX,QAAgB,EAChB,eAAiC,EACjC,OAA6B,EAAA;AAE7B,IAAA,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;AAC/B,IAAA,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;AACnC,IAAA,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IAElB,IAAI,eAAe,IAAI,IAAI,EAAE;AAC3B,QAAA,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,eAAe,CAAC,CAAC;AACnC,KAAA;IAED,MAAM,2BAA2B,EAAE,CAAC;IAEpC,MAAM,MAAM,CAAC,sBAAsB,EAAE;QACnC,EAAE;QACF,GAAG;QACH,QAAQ;AACR,QAAA,OAAO,EAAE,OAAO,KAAA,IAAA,IAAP,OAAO,KAAP,KAAA,CAAA,GAAA,OAAO,GAAI,EAAE;AACvB,KAAA,CAAC,CAAC;AACL;;;;"} {"version":3,"file":"index.mjs","sources":["../index.ts"],"sourcesContent":[null],"names":[],"mappings":";;;AAqBA,IAAK,WAGJ,CAAA;AAHD,CAAA,UAAK,WAAW,EAAA;AACZ,IAAA,WAAA,CAAA,UAAA,CAAA,GAAA,mBAA8B,CAAA;AAC9B,IAAA,WAAA,CAAA,UAAA,CAAA,GAAA,oBAA+B,CAAA;AACnC,CAAC,EAHI,WAAW,KAAX,WAAW,GAGf,EAAA,CAAA,CAAA,CAAA;AAMD,MAAM,QAAQ,GAAgD,IAAI,GAAG,EAAE,CAAC;AACxE,MAAM,YAAY,GAA8B,IAAI,GAAG,EAAE,CAAC;AAE1D,MAAM,aAAa,GAAG,CAAC,KAAkB,EAAE,EAAU,KAAc,CAAG,EAAA,KAAK,CAAI,CAAA,EAAA,EAAE,EAAE,CAAC;AAEpF,eAAe,qBAAqB,CAAC,KAAkB,EAAA;AACnD,IAAA,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QACzB,OAAO;AACV,KAAA;IACD,OAAO,SAAS,CAAC,MAAM,CAAwC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,KAAI;QAClF,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACtC,QAAA,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE;YAC/B,OAAO,CAAC,OAAO,CAAC,CAAC;AACpB,SAAA;AACL,KAAC,CAAC,CAAC;AACP,CAAC;AAEc,eAAe,MAAM,CAAC,GAAW,EAAE,QAAgB,EAAE,eAAiC,EAAE,eAA6B,EAAE,OAAgC,EAAA;AAClK,IAAA,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;AAC/B,IAAA,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;AACnC,IAAA,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;AAElB,IAAA,IAAI,eAAe,EAAE;QACjB,MAAM,OAAO,GAAG,aAAa,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AACxD,QAAA,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;AAC1C,KAAA;AAED,IAAA,IAAI,eAAe,EAAE;QACjB,MAAM,OAAO,GAAG,aAAa,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AACxD,QAAA,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;AAC1C,KAAA;AAED,IAAA,MAAM,qBAAqB,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;AAClD,IAAA,MAAM,qBAAqB,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;AAElD,IAAA,OAAO,MAAM,MAAM,CAAe,sBAAsB,EAAE;QACtD,EAAE;QACF,GAAG;QACH,QAAQ;AACR,QAAA,OAAO,EAAE,OAAO,KAAA,IAAA,IAAP,OAAO,KAAP,KAAA,CAAA,GAAA,OAAO,GAAI,EAAE;AACzB,KAAA,CAAC,CAAC;AACP;;;;"}

@ -1,52 +1,73 @@
import { invoke } from "@tauri-apps/api/tauri"; import { invoke } from '@tauri-apps/api/tauri';
import { appWindow } from "@tauri-apps/api/window"; import { appWindow } from '@tauri-apps/api/window';
interface ProgressPayload { interface BaseHandlerData {
id: number; id: number;
progress: number;
total: number;
} }
type ProgressHandler = (progress: number, total: number) => void; interface ProgressHandlerData extends BaseHandlerData {
const handlers: Map<number, ProgressHandler> = new Map(); progress: number;
let listening = false; total: number;
}
async function listenToUploadEventIfNeeded(): Promise<void> {
if (listening) { interface SizeHandlerData extends BaseHandlerData {
return await Promise.resolve(); size: number;
} }
return await appWindow
.listen<ProgressPayload>("upload://progress", ({ payload }) => { interface ResponseData {
const handler = handlers.get(payload.id); text: string;
if (handler != null) { status: number;
handler(payload.progress, payload.total); }
}
}) enum UploadEvent {
.then(() => { progress = 'upload://progress',
listening = true; fileSize = 'upload://file-size',
}
type EventId = `${UploadEvent}-${number}`;
type ProgressHandler = (data: ProgressHandlerData) => unknown;
type SizeHandler = (data: SizeHandlerData) => unknown;
const handlers: Map<EventId, ProgressHandler | SizeHandler> = new Map();
const listeningMap: Map<UploadEvent, boolean> = new Map();
const getIdForEvent = (event: UploadEvent, id: number): EventId => `${event}-${id}`;
async function listenToEventIfNeeded(event: UploadEvent) {
if (listeningMap.get(event)) {
return;
}
return appWindow.listen<SizeHandlerData & ProgressHandlerData>(event, ({ payload }) => {
const eventId = getIdForEvent(event, payload.id);
const handler = handlers.get(eventId);
if (typeof handler === 'function') {
handler(payload);
}
}); });
} }
export default async function upload( export default async function upload(url: string, filePath: string, progressHandler?: ProgressHandler, fileSizeHandler?: SizeHandler, headers?: Record<string, string>) {
url: string, const ids = new Uint32Array(1);
filePath: string, window.crypto.getRandomValues(ids);
progressHandler?: ProgressHandler, const id = ids[0];
headers?: Map<string, string>
): Promise<void> { if (progressHandler) {
const ids = new Uint32Array(1); const eventId = getIdForEvent(UploadEvent.progress, id);
window.crypto.getRandomValues(ids); handlers.set(eventId, progressHandler);
const id = ids[0]; }
if (progressHandler != null) { if (fileSizeHandler) {
handlers.set(id, progressHandler); const eventId = getIdForEvent(UploadEvent.fileSize, id);
} handlers.set(eventId, fileSizeHandler);
}
await listenToUploadEventIfNeeded();
await listenToEventIfNeeded(UploadEvent.progress);
await invoke("plugin:upload|upload", { await listenToEventIfNeeded(UploadEvent.fileSize);
id,
url, return await invoke<ResponseData>('plugin:upload|upload', {
filePath, id,
headers: headers ?? {}, url,
}); filePath,
headers: headers ?? {},
});
} }

@ -38,6 +38,18 @@ struct ProgressPayload {
total: u64, total: u64,
} }
#[derive(Clone, Serialize)]
struct FileSizePayload {
id: u32,
size: u64,
}
#[derive(Clone, Serialize)]
struct ResponseData {
text: String,
status: u16,
}
#[command] #[command]
async fn upload<R: Runtime>( async fn upload<R: Runtime>(
window: Window<R>, window: Window<R>,
@ -45,15 +57,33 @@ async fn upload<R: Runtime>(
url: &str, url: &str,
file_path: &str, file_path: &str,
headers: HashMap<String, String>, headers: HashMap<String, String>,
) -> Result<serde_json::Value> { ) -> Result<ResponseData> {
// Read the file // Read the file
let parsed_url = url::Url::parse(url).unwrap();
let file = File::open(file_path).await?; let file = File::open(file_path).await?;
let file_metadata = file.metadata().await?;
let file_size = file_metadata.len();
let _ = window.emit(
"upload://file-size",
FileSizePayload {
size: file_size,
id,
},
);
// Create the request and attach the file to the body // Create the request and attach the file to the body
let client = reqwest::Client::new(); let client = reqwest::Client::new();
let mut request = client.post(url).body(file_to_body(id, window, file)); let mut request = client.put(url).body(file_to_body(id, window, file));
request = request.header("Content-Length", file_size);
request = request.header("User-Agent", "Tauri/1.0");
request = request.header("Accept-Encoding", "gzip, deflate, br");
request = request.header("Accept", "*/*");
request = request.header("Connection", "keep-alive");
let host = parsed_url.host().unwrap();
request = request.header("Host", host.to_string());
// Loop trought the headers keys and values // Loop trough the headers keys and values
// and add them to the request object. // and add them to the request object.
for (key, value) in headers { for (key, value) in headers {
request = request.header(&key, value); request = request.header(&key, value);
@ -61,7 +91,9 @@ async fn upload<R: Runtime>(
let response = request.send().await?; let response = request.send().await?;
response.json().await.map_err(Into::into) let status = response.status().as_u16();
let text = response.text().await?;
Ok(ResponseData { text, status })
} }
fn file_to_body<R: Runtime>(id: u32, window: Window<R>, file: File) -> reqwest::Body { fn file_to_body<R: Runtime>(id: u32, window: Window<R>, file: File) -> reqwest::Body {

Loading…
Cancel
Save