use drission::prelude::*;
const PAGE: &str = r#"<!doctype html><html><head><meta charset="utf-8"><title>upload</title></head>
<body>
<h1>upload demo</h1>
<!-- 隐藏的真正 input,由按钮代理唤起:这就是最常见的"自然上传"场景 -->
<input id="real" type="file" style="display:none">
<button id="pick">选择文件</button>
<div id="picked">none</div>
<!-- 可直接定位的 input:传统写法用 set_files 直接赋值 -->
<input id="direct" type="file">
<div id="direct_name">none</div>
<script>
const real = document.getElementById('real');
document.getElementById('pick').addEventListener('click', () => {
real.click();
});
real.addEventListener('change', () => {
document.getElementById('picked').textContent =
Array.from(real.files).map(f => f.name).join(',') || 'none';
});
const direct = document.getElementById('direct');
direct.addEventListener('change', () => {
document.getElementById('direct_name').textContent =
Array.from(direct.files).map(f => f.name).join(',') || 'none';
});
</script>
</body></html>"#;
async fn file_name(tab: &Tab, id: &str) -> drission::Result<String> {
let v = tab
.run_js(&format!(
"(document.getElementById('{id}').files[0]||{{}}).name||''"
))
.await?;
Ok(v.as_str().unwrap_or_default().to_string())
}
#[tokio::main]
async fn main() -> drission::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into()),
)
.init();
let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("target")
.join("drission-upload");
tokio::fs::create_dir_all(&dir).await?;
let page_path = dir.join("page.html");
tokio::fs::write(&page_path, PAGE).await?;
let file_a = dir.join("alpha.txt");
let file_b = dir.join("bravo.txt");
let file_c = dir.join("charlie.txt");
tokio::fs::write(&file_a, b"alpha").await?;
tokio::fs::write(&file_b, b"bravo").await?;
tokio::fs::write(&file_c, b"charlie").await?;
let (pa, pb, pc) = (
file_a.to_string_lossy().to_string(),
file_b.to_string_lossy().to_string(),
file_c.to_string_lossy().to_string(),
);
let url = format!("file://{}", page_path.display());
let headless = std::env::var("HL").map(|v| v != "0").unwrap_or(true);
println!("[*] 启动 Camoufox(headless={headless})…");
let browser = Browser::launch(BrowserOptions::new().headless(headless)).await?;
let tab = browser.latest_tab().await?;
let ok_get = tab.get(&url).await?;
println!("[*] get({url}) ok={ok_get}");
let mut loaded = false;
for _ in 0..20 {
if tab.html().await?.len() >= 80 {
loaded = true;
break;
}
tab.wait().secs(0.2).await;
}
if !loaded {
return Err(drission::Error::msg(format!(
"页面未正确加载(疑似沙箱拒读):{url}"
)));
}
tab.set().upload_files(&[pa.as_str()]).await?;
tab.ele("#pick").await?.click().await?;
let inputted = tab
.wait()
.upload_paths_inputted(Some(std::time::Duration::from_secs(5)))
.await?;
tab.wait().secs(0.2).await; let name1 = file_name(&tab, "real").await?;
let picked1 = tab.ele("#picked").await?.text().await?;
let ok1 = inputted && name1 == "alpha.txt" && picked1 == "alpha.txt";
println!(
"[1] 自然上传·分步: inputted={inputted} files[0]={name1:?} #picked={picked1:?} (ok={ok1})"
);
let done2 = tab
.ele("#pick")
.await?
.click_to_upload(&[pb.as_str()], None)
.await?;
tab.wait().secs(0.2).await;
let name2 = file_name(&tab, "real").await?;
let picked2 = tab.ele("#picked").await?.text().await?;
let ok2 = done2 && name2 == "bravo.txt" && picked2 == "bravo.txt";
println!("[2] 自然上传·一步: done={done2} files[0]={name2:?} #picked={picked2:?} (ok={ok2})");
tab.ele("#direct").await?.set_files(&[pc.as_str()]).await?;
tab.wait().secs(0.2).await;
let name3 = file_name(&tab, "direct").await?;
let dname3 = tab.ele("#direct_name").await?.text().await?;
let ok3 = name3 == "charlie.txt" && dname3 == "charlie.txt";
println!("[3] 传统直接赋值: files[0]={name3:?} #direct_name={dname3:?} (ok={ok3})");
let pass = ok_get && ok1 && ok2 && ok3;
println!(
"\n==== {} ====",
if pass {
"ALL CHECKS PASSED"
} else {
"SOME CHECKS FAILED"
}
);
browser.quit().await?;
if pass {
Ok(())
} else {
Err(drission::Error::msg("file_upload 自验证未通过"))
}
}