JavaScript: var startTime = 0; var endTime = 0; var totalFrames = 0; var image = null; var getNewImg = function() { const fpstext = document.getElementById("fps"); // var imgobj = document.getElementById("testimg"); // var url = null; const imgcanvas = document.getElementById("imgcanvas"); const ctx = imgcanvas.getContext("2d"); // imgobj.onload = () => { // totalFrames++; // endTime = new Date().valueOf(); // var useTime = endTime - startTime; // fps = totalFrames * 1000 / useTime; // fpstext.innerText = fps; // // 不再需要读取该 blob,因此释放该对象 // URL.revokeObjectURL(url); // setTimeout(getNewImg, 0); // }; //const requestUrl = "imgrawdata://localhost?n=" + totalFrames + "&b2"; const requestUrl = "https://imgrawdata.localhost/?n=" + totalFrames + "&b2"; //const requestUrl = "http://127.0.0.1:8080/img.jpg?n=" + totalFrames; //obj.src = requestUrl; const request = new Request(requestUrl, { method: "GET" }); // taurihttp.fetch(requestUrl, { // method: 'GET', // responseType : taurihttp.ResponseType.Binary // }) if (startTime == 0) { startTime = new Date().valueOf(); } fetch(request) // .then((response) => {}) .then((response) => response.blob()) .then((blob) => { // url = URL.createObjectURL(blob); // imgobj.src = url; blob.arrayBuffer().then(buffer=>{ var array = new Uint8ClampedArray(buffer); if (image == null) { image = new ImageData(array, 5120, 2880); //image = new ImageData(array, 2000, 1125); } else { image.data = array; } ctx.putImageData(image, 0, 0); totalFrames++; endTime = new Date().valueOf(); var useTime = endTime - startTime; fps = totalFrames * 1000 / useTime; fpstext.innerText = fps; setTimeout(getNewImg, 0); }); }) .catch((error) => { //console.error(error); //setTimeout(getNewImg, 0); }); }; setTimeout(getNewImg, 100); HTML: <p id="fps"></p> <img id="testimg" src="" /> Rust: use tauri::http::{header, ResponseBuilder}; use std::fs::{self, read}; use image::io::Reader as ImageReader; static mut RGBA_IMG_DATA: Vec = vec![]; fn main() { tauri::Builder::default() ... .register_uri_scheme_protocol("imgrawdata", |app, request| { let res_not_img = ResponseBuilder::new() .status(404) .body(Vec::new()); if request.method() != "GET" { return res_not_img; } let uri = request.uri(); let start_pos = match uri.find("?n=") { Some(_pos) => _pos + 3, None => return res_not_img, }; let end_pos = match uri.find("&") { Some(_pos) => _pos, None => return res_not_img, }; let entry_num: usize = match &uri[start_pos..end_pos].parse() { Ok(_i) => *_i, Err(_) => return res_not_img, }; println!("Request: n={}", entry_num); if unsafe { RGBA_IMG_DATA.len() } == 0 { let target_file = "E:\\tauri-app\\img.jpg"; let img = image::open(target_file).unwrap(); let rgba_img = image::DynamicImage::ImageRgb8(img.into()).into_rgba8(); unsafe { RGBA_IMG_DATA = rgba_img.to_vec() }; } println!("RGBA_IMG_DATA len:{}", unsafe { RGBA_IMG_DATA.len() }); let local_img = if unsafe { RGBA_IMG_DATA.len() } != 0 { tauri::http::ResponseBuilder::new() .header("Access-Control-Allow-Origin", "*") .header("ETag", entry_num.to_string()) .header("Cache-control", "no-store") .mimetype("application/octet-stream") .header("Content-Length", unsafe { RGBA_IMG_DATA.len() }) .status(200) .body(unsafe { RGBA_IMG_DATA.clone() }) } else { res_not_img }; local_img }) .run(tauri::generate_context!()) .expect("error while running tauri application"); }