Rust MIME多部分数据处理库mime_multipart的使用,支持高效解析和生成MIME multipart格式

Rust MIME多部分数据处理库mime_multipart的使用,支持高效解析和生成MIME multipart格式

MIT licensed Apache-2.0 licensed

Rust库用于MIME多部分解析、构建和流式处理

特性

  • 从流中解析,而不是在内存中,因此不会占用过多内存
  • 将识别为文件的部分(通过部分的Content-Disposition头或手动重写)流式传输到磁盘上的文件
  • 使用缓冲流
  • 允许您构建和流式传输多部分作为节点(Node)的向量,其中一些可以是文件,其他可以是嵌套的多部分部分

安装

在项目目录中运行以下Cargo命令:

cargo add mime_multipart

或者在Cargo.toml中添加以下行:

mime_multipart = "0.6.1"

示例代码

use mime_multipart::{Node, Multipart, Part};
use std::fs::File;
use std::io::Write;

fn main() -> std::io::Result<()> {
    // 创建一个简单的多部分消息
    let mut multipart = Multipart::new();
    
    // 添加文本部分
    multipart.push(
        Node::Part(
            Part::new()
                .set_body("这是文本部分".to_string())
                .set_content_type("text/plain".parse().unwrap())
        )
    );
    
    // 添加文件部分
    let mut file_part = Part::new()
        .set_content_type("image/jpeg".parse().unwrap())
        .set_content_disposition("attachment; filename=\"example.jpg\"".parse().unwrap());
    
    // 创建临时文件并写入数据
    let mut temp_file = File::create("example.jpg")?;
    temp_file.write_all(b"模拟的JPEG文件数据")?;
    
    // 将文件添加到多部分
    multipart.push(
        Node::File("example.jpg".to_string())
    );
    
    // 生成MIME多部分数据
    let mut output = Vec::new();
    multipart.write_to(&mut output)?;
    
    println!("Generated MIME multipart data:");
    println!("{}", String::from_utf8_lossy(&output));
    
    Ok(())
}
// 解析MIME多部分数据的示例
use mime_multipart::{read_multipart, PartHandler};
use std::io::Cursor;

struct MyHandler;

impl PartHandler for MyHandler {
    fn handle(&mut self, part: mime_multipart::Part) -> std::io::Result<()> {
        println!("Received part with content type: {}", part.content_type());
        if let Some(disposition) = part.content_disposition() {
            println!("Content disposition: {}", disposition);
        }
        
        // 读取部分内容
        let mut body = Vec::new();
        part.read_to_end(&mut body)?;
        println!("Body length: {} bytes", body.len());
        
        Ok(())
    }
}

fn parse_example() -> std::io::Result<()> {
    // 模拟MIME多部分数据
    let data = "--boundary\r\n\
                Content-Type: text/plain\r\n\
                \r\n\
                This is a text part\r\n\
                --boundary\r\n\
                Content-Type: image/jpeg\r\n\
                Content-Disposition: attachment; filename=\"example.jpg\"\r\n\
                \r\n\
                JPEG file data goes here\r\n\
                --boundary--\r\n";
    
    let cursor = Cursor::new(data.as_bytes());
    let mut handler = MyHandler;
    
    read_multipart(cursor, "boundary", &mut handler)?;
    
    Ok(())
}

完整示例demo

下面是一个结合生成和解析MIME多部分数据的完整示例:

use mime_multipart::{Node, Multipart, Part, read_multipart, PartHandler};
use std::fs::{File, remove_file};
use std::io::{Write, Cursor, Read};

// 自定义PartHandler用于处理解析的各个部分
struct ExampleHandler;

impl PartHandler for ExampleHandler {
    fn handle(&mut self, part: mime_multipart::Part) -> std::io::Result<()> {
        // 获取并打印内容类型
        println!("Processing part with content type: {}", part.content_type());
        
        // 获取并打印内容处置信息(如果有)
        if let Some(disposition) = part.content_disposition() {
            println!("Content disposition: {}", disposition);
        }
        
        // 读取部分内容到内存
        let mut content = Vec::new();
        part.read_to_end(&mut content)?;
        
        // 打印内容长度和前50个字符(如果是文本)
        println!("Content length: {} bytes", content.len());
        if content.len() > 0 {
            println!("First 50 bytes: {:?}", &content[..50.min(content.len())]);
        }
        
        Ok(())
    }
}

fn main() -> std::io::Result<()> {
    // 1. 创建并生成MIME多部分数据
    let mut multipart = Multipart::new();
    
    // 添加文本部分
    multipart.push(
        Node::Part(
            Part::new()
                .set_body("This is a text part".to_string())
                .set_content_type("text/plain".parse().unwrap())
                .set_content_disposition("inline".parse().unwrap())
        )
    );
    
    // 创建并添加文件部分
    let filename = "example.txt";
    let mut file = File::create(filename)?;
    file.write_all(b"This is some file content")?;
    
    multipart.push(
        Node::File(filename.to_string())
    );
    
    // 将多部分数据写入内存缓冲区
    let mut buffer = Vec::new();
    multipart.write_to(&mut buffer)?;
    
    // 2. 解析刚刚生成的MIME多部分数据
    println!("\nParsing the generated multipart data:");
    let cursor = Cursor::new(buffer);
    let mut handler = ExampleHandler;
    read_multipart(cursor, multipart.get_boundary(), &mut handler)?;
    
    // 清理临时文件
    remove_file(filename)?;
    
    Ok(())
}

许可证

双许可:

  • MIT许可证
  • Apache许可证2.0版本

您可以选择其中任一许可证。


1 回复

Rust MIME多部分数据处理库mime_multipart使用指南

mime_multipart是一个用于处理MIME多部分数据的Rust库,支持高效解析和生成符合RFC 2046规范的multipart格式数据。

主要特性

  • 支持解析multipart/form-data和multipart/mixed等多种MIME多部分类型
  • 提供生成MIME多部分数据的功能
  • 内存高效,适合处理大文件
  • 完全兼容RFC 2046标准

安装

在Cargo.toml中添加依赖:

[dependencies]
mime_multipart = "0.6"

基本使用方法

解析MIME多部分数据

use mime_multipart::Node;
use std::fs::File;

fn parse_multipart() -> Result<(), Box<dyn std::error::Error>> {
    // 从文件读取multipart数据
    let mut file = File::open("multipart_data.txt")?;
    
    // 解析multipart数据
    let node = Node::parse(&mut file)?;
    
    // 遍历各部分
    if let Node::Multipart(nodes) = node {
        for part in nodes {
            println!("Part content type: {:?}", part.content_type());
            if let Node::Data(data) = part {
                println!("Part data length: {}", data.len());
            }
        }
    }
    
    Ok(())
}

生成MIME多部分数据

use mime_multipart::{Node, generate_boundary};
use std::io::Write;

fn create_multipart() -> Result<(), Box<dyn std::error::Error>> {
    // 创建一个新的multipart节点
    let mut multipart = Node::new_multipart("form-data");
    
    // 添加文本字段
    let mut field1 = Node::new_data("text/plain", "field1 value".as_bytes().to_vec());
    field1.add_header("Content-Disposition", "form-data; name=\"field1\"")?;
    multipart.push(field1);
    
    // 添加文件字段
    let mut field2 = Node::new_data("application/octet-stream", "file content".as_bytes().to_vec());
    field2.add_header("Content-Disposition", "form-data; name=\"file\"; filename=\"example.txt\"")?;
    multipart.push(field2);
    
    // 生成边界字符串
    let boundary = generate_boundary();
    
    // 写入到输出流
    let mut out = Vec::new();
    multipart.write_to(&mut out, &boundary)?;
    
    println!("Generated multipart data:\n{}", String::from_utf8_lossy(&out));
    
    Ok(())
}

高级用法

处理大文件

use mime_multipart::{Node, Part};
use std::fs::File;
use std::io::{self, Read};

fn process_large_file() -> Result<(), Box<dyn std::error::Error>> {
    // 创建multipart节点
    let mut multipart = Node::new_multipart("form-data");
    
    // 添加大文件作为流式处理
    let mut file_part = Part::new("application/octet-stream");
    file_part.add_header("Content-Disposition", "form-data; name=\"large_file\"; filename=\"bigdata.bin\"")?;
    
    // 设置数据提供者闭包
    file_part.set_reader(|| {
        Box::new(File::open("bigdata.bin").map_err(|e| io::Error::new(io::ErrorKind::Other, e))?)
    });
    
    multipart.push(Node::Part(file_part));
    
    // 写入到输出流
    let boundary = generate_boundary();
    let mut out = File::create("output.multipart")?;
    multipart.write_to(&mut out, &boundary)?;
    
    Ok(())
}

自定义边界和头信息

use mime_multipart::{Node, Part};

fn custom_headers() -> Result<(), Box<dyn std::error::Error>> {
    let mut multipart = Node::new_multipart("mixed");
    
    // 自定义边界
    let boundary = "my_custom_boundary_12345";
    
    // 添加带有自定义头部的部分
    let mut part1 = Part::new("text/plain");
    part1.add_header("Content-Disposition", "inline")?;
    part1.add_header("X-Custom-Header", "value123")?;
    part1.set_body("This is the first part".as_bytes().to_vec());
    
    multipart.push(Node::Part(part1));
    
    // 写入到Vec<u8>
    let mut output = Vec::new();
    multipart.write_to(&mut output, boundary)?;
    
    println!("Custom multipart:\n{}", String::from_utf8_lossy(&output));
    
    Ok(())
}

完整示例:HTTP文件上传服务

下面是一个完整的HTTP服务器示例,演示如何使用mime_multipart处理文件上传:

use mime_multipart::{Node, Part};
use std::io::{self, Read};
use std::net::{TcpListener, TcpStream};
use std::thread;

fn handle_client(mut stream: TcpStream) -> io::Result<()> {
    let mut buffer = Vec::new();
    stream.read_to_end(&mut buffer)?;
    
    // 解析multipart数据
    let node = Node::parse(&mut buffer.as_slice())?;
    
    if let Node::Multipart(parts) = node {
        for part in parts {
            if let Node::Part(p) = part {
                println!("Processing part with headers: {:?}", p.headers());
                
                if let Some(disposition) = p.headers().get("Content-Disposition") {
                    if disposition.contains("filename") {
                        // 处理文件上传部分
                        let mut file_content = Vec::new();
                        p.read_to(&mut file_content)?;
                        println!("Received file with size: {} bytes", file_content.len());
                    } else {
                        // 处理普通表单字段
                        let mut field_content = Vec::new();
                        p.read_to(&mut field_content)?;
                        println!("Form field: {}", String::from_utf8_lossy(&field_content));
                    }
                }
            }
        }
    }
    
    // 返回响应
    let response = "HTTP/1.1 200 OK\r\n\r\nUpload received";
    stream.write_all(response.as_bytes())?;
    
    Ok(())
}

fn main() -> io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:8080")?;
    println!("Server running on port 8080");
    
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                thread::spawn(move || {
                    handle_client(stream).unwrap_or_else(|e| eprintln!("Error: {}", e));
                });
            }
            Err(e) => {
                eprintln!("Connection failed: {}", e);
            }
        }
    }
    
    Ok(())
}

注意事项

  1. 处理大文件时建议使用流式API(set_reader)以避免内存问题
  2. 边界字符串应当足够随机以避免与内容冲突
  3. 解析时库会自动处理各种换行符(CRLF, LF)
  4. 对于form-data类型,Content-Disposition头部是必需的

这个库非常适合处理HTTP文件上传、邮件附件解析等需要处理MIME多部分数据的场景。

回到顶部