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

4
我正在按照《Rust书籍》中的多线程Web服务器示例(https://doc.rust-lang.org/book/ch20-00-final-project-a-web-server.html)进行操作。
发送文本文件(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();

结果是浏览器无法播放该文件。事实上,如果我设法下载发送的文件,它也会出现损坏。

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


你假设内容是有效的UTF8,这对于二进制文件(MP3)来说并不是一个好的假设。尝试打印“contents”,你应该什么也看不到。 - Alex Huszagh
显然,它可以很好地处理内部的空字符,但是可能会破坏没有起始字节或反向的连续字节。不要假设二进制数据是UTF8。 - Alex Huszagh
1个回答

6

您的问题是假设二进制数据采用UTF-8编码,这是一个错误的假设。字节不是字符串,而二进制数据也不是UTF-8格式。例如,MP3文件中会包含内部null字符值,这在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";

如果我们试图将其转换为UTF-8,则应该会出现错误。如果我们使用 from_utf8_lossy,它会将任何无效的UTF-8数据转换为U+FFFD,根据文档

字符串由字节(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();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接