Rust - Rocket 和 Vue 简单实现分片上传

9 篇文章 3 订阅

第一次写分片上传,记录一下~
刚学Rust没多久,代码有点乱,勉强可以浅看一看
还没实现 续传,未来可能更新这篇文章
在这里插入图片描述

首先

确定临时文件目录

fn 分片_root_path<P: AsRef<Path>>( path: P)-> PathBuf {
	Path::new("./public/分片").join(path)
}

确定trait(接口)

pub trait 分片 {
	
	/**
	#推入一个分片
	 */
	fn push_data(&self,piece_number:usize,piece_data:&[u8]);

	/**
	#合成文件
	**return** : [`None`]:合成失败 , [`Some`] (文件,总分片数)
	 */
	fn make_file(&self)->Option<(File,usize)>;
	/**
	#删除所有分片
	 */
	fn delete_all_piece(self);
}

确定操作器

/**
    建立以 id 命名的目录

	分片文件名从 1 开始

	0 文件名是最后合成的文件名
 */
pub struct 分片操作器(String);//.0 是id

确定构造器

///构建一个 [`分片操作器`]
pub struct 分片构造器;
impl 分片构造器 {//代码比较简单一起写在这里啦
	pub fn new(id:String) -> 分片操作器 {
		DirBuilder::new().create(分片_root_path(&id));
		分片操作器(id)
	}
}

一些操作用的函数 , 注意是函数哦~

impl 分片操作器{
	pub fn path(&self) -> PathBuf {//获取路径,这是方法,图方便用的
		分片_root_path(&self.0)
	}
	fn delete_all_piece(id:&String){//删除
		fs::remove_dir_all(分片_root_path(id));
	}
	///piece_number 是 第piece_number个分片,用来确定文件名
	fn create_piece(id:&String,piece_number:usize,data:&[u8]){//创建一个分片
		let path_buf = 分片_root_path(id);
		// DirBuilder::new().create(path_buf.as_path());//由分片构造器创建目录
		let mut piece_file =
			File::create(path_buf.join(piece_number.to_string()))
				.unwrap();//上面必然存在目录,所以这个不用担心啦
		piece_file.write(data);
	}
	fn make_file(id:&String) -> Option<(File,usize)>{//( 合成后的文件 , 总分片数 )
		let path = 分片_root_path(id);
		let mut list:Vec<usize> = Vec::new();
		if let Ok(v) = read_dir(&path) {//这里比较乱,建议复制到IDE中去看
		//大概功能就是 遍历 路径下的全部文件,把他们推入 list 中,用于有序合成文件
			for v in v {
				if let Ok(v) = v {
					let file_name = v.file_name().into_string().unwrap();
					let i = file_name.parse::<usize>().unwrap();
					if i!=0{
						(&mut list).push(i);
					}
				}
			}
		}else { //Err
			return None;
		}
		list.sort();//排序

		let mut file = File::create(&path.join("0"))
			.unwrap();//上面有检查,所以不用担心这个啦
		let mut buf = [0_u8;1024];//缓冲区
		for v in &list {
			let mut f = File::open(&path.join(v.to_string()))
				.unwrap();//由上面找到的,所以必然存在,不用担心啦~
			while let Ok(v) = f.read(&mut buf){
				if v==0 {break;}
				(&mut file).write(&buf[0..v]);
			};
		}
		Some((file,list.len()))
	}
	
}

实现 分片trait

impl 分片 for 分片操作器 {
	fn push_data(&self, piece_number: usize, piece_data: &[u8]) {
	//这里其实我想去掉 piece_number ,通过扫描目录下的文件,来确定文件名的,但是又感觉限制操作了
		分片操作器::create_piece(&self.0,piece_number,piece_data)
	}
	
	fn make_file(&self) -> Option<(File, usize)> {
		分片操作器::make_file(&self.0)
	}
	
	fn delete_all_piece(self) {
		分片操作器::delete_all_piece(&self.0);
	}
}

最后是 业务 逻辑

#[derive(FromForm)]
pub struct PieceForm<'a> {
	id:String,
	piece_number:String,// 0:完成分片,可以合成
	file_data:TempFile<'a>
}

#[post("/file",data = "<file>")]
pub async fn post_file_cs(mut file: Form<PieceForm<'_>>) -> std::io::Result<String> {
	println!("{}",&file.id);
	println!("{}",&file.piece_number);
	let piece_number = file.piece_number.clone();
	let piece = 分片构造器::new(file.id.clone());
	if file.piece_number.eq("0") {
		if let Some((file,i))= piece.make_file() {
			
			println!("文件合成完成!");
			println!("总分片数:{}",i);
			println!("文件:{:#?}",file);
			return Ok(format!("Ok:合成成功,总片数:[{}]",i));
		}
		return Ok(format!("Err:合成失败"))
	}
	
	file.file_data.persist_to(piece
		.path().join(&piece_number)).await?;//它居然会直接建立文件...让我的函数显得很没用耶
	
	Ok(piece_number)
}

这是前端代码 (Vue)

<template><div>
	
	<input type="file" ref="file" >
	<n-button @click="请求">请求</n-button>
	
</div></template>

<script lang="ts" setup>
import { ref , reactive , computed ,  watchEffect , defineProps , withDefaults , defineEmits , defineExpose , useSlots , useAttrs, onMounted} from 'vue';
import { useStore } from "vuex";
import { useRouter, useRoute } from "vue-router";
import axios from "axios"
import {Md5} from "ts-md5"

const router = useRouter();
const file: File | any = ref({});

async function 请求() {
	let f: File | any = file.value.files[0];
	console.log(f);
	if(f==undefined||f==null){
		return;
	}
	const piece_size = 1024*256;//每片大小 256KiB
	// const piece_size = 10;//测试用的
	const len = f.size;//文件大小
	const name = f.name;//文件名
	const piece_number = Math.ceil(len/piece_size); //片数
	const id =name + "+" + new Md5().appendAsciiStr(
			await f.text()).end(false) as string;
	
	console.log("分片总数:"+piece_number);
	console.log("文件名:"+f.name);
	for(let i = 1; i <= piece_number; i++){
		if(i==piece_number){	//最后一片上传
			const form = new FormData();
			form.set("id",id)
			form.set("piece_number",piece_number.toString());
			form.set("file_data",f
					.slice((piece_number-1)*piece_size
							,(piece_number-1)*piece_size+len%piece_size));
			const a = await axios.post("/api/rust/cs/file",form);
			console.log(a);
			break;
		}
	   const form = new FormData();
	   form.set("id",id)
	   form.set("piece_number",i.toString());
	   form.set("file_data",f.slice((i-1)*piece_size,i*piece_size));
		const a = await axios.post("/api/rust/cs/file",form);
	   console.log(a);
  }
	//告诉后端完成传输,允许合成文件
	const form = new FormData();
	form.set("id",id)
	form.set("piece_number","0");
	form.set("file_data",f.slice(0,0));
	const a = await axios.post("/api/rust/cs/file",form);
	console.log(a);
	
}

</script>

Rust 写起来就是得劲!
以上代码并没有 检查逻辑问题 和 完善代码结构(前端代码可以证明,哈哈),大家浅看就好~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艾仪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值