如何在Rust中正确格式化带有媒体文件的HTTP响应

Dav*_*vid 2 http rust

我正在跟踪Rust本书(https://doc.rust-lang.org/book/ch20-00-final-project-a-web-server.html)中的多线程Web服务器示例。

发送文本文件(html)效果很好。购买尝试发送二进制(mp3)文件会在浏览器上显示错误。

要发送MP3文件,我正在尝试以下代码。我认为问题出在将内容转换为字符串。我试过更改标题,尝试另一种Content-Type似乎没有任何用。

let sent_bytes = contents.len();
let contents = &String::from_utf8_lossy(&contents[..]);
let response = format!("HTTP/1.0 200 OK\r\nContent-Type: audio/mpeg\r\nContent-Length: {}\r\n\r\n{}",
            sent_bytes,
            contents
        );
writer.write_all(response.as_bytes()).unwrap();
eprintln!("sent {}bytes\n", sent_bytes);
writer.flush().unwrap();
Run Code Online (Sandbox Code Playgroud)

结果是浏览器无法重现该文件。实际上,如果我设法下载发送的文件,则它已损坏。

我应该如何编码文件以将其发送到浏览器?

Ale*_*agh 5

您的问题是假设二进制数据采用UTF-8编码,这是一个错误的假设。字节不是字符串,二进制数据不是UTF-8。例如,MP3将具有内部的空字符值,在UTF-8中无效。同样,某些字节模式无效(特别是无效的开始/继续字节模式),Rust尝试将MP3数据表示为UTF-8字符串时将其删除。

举例来说:

const MP3: &[u8] = b"ID3\x03\x00\x00\x00\x00\x00fTCON\x00\x00\x00\n\x00\x00\x00CinematicTALB\x00\x00\x00\x16\x00\x00\x00YouTube A";
Run Code Online (Sandbox Code Playgroud)

如果我们尝试将其转换为UTF-8,我们应该得到一个错误。如果使用from_utf8_Lossy,它将U+FFFD按照文档将任何无效的UTF-8数据转换为。

字符串由字节(u8)组成,而字节片(&[u8])由字节组成,因此此函数在两者之间进行转换。并非所有的字节片都是有效的字符串,但是:字符串必须是有效的UTF-8。在此转换期间,from_utf8_lossy()将用U + FFFD REPLACEMENT CHARACTER替换任何无效的UTF-8序列,如下所示:

您要做的是将HTML标头格式设置为UTF-8,将其转换为原始字节,然后在其后附加原始MP3数据。以下代码将为您做到这一点:

let contents = &MP3[..];
let response = format!("HTTP/1.0 200 OK\r\nContent-Type: audio/mpeg\r\nContent-Length: {}\r\n\r\n",
    contents.len(),
);
writer.write_all(response.as_bytes()).unwrap();
writer.write_all(contents).unwrap();
writer.flush().unwrap();
Run Code Online (Sandbox Code Playgroud)