Search code examples
httpioruststreamreqwest

How do I tell std::io::copy in Rust to stop reading and finish up writing?


I'm doing a direct download of an MP3 audio stream via Rust. As this stream is indefinite, I want to be able to cancel it early to save what I have downloaded so far. Currently, I do this by pressing CTRL + C to stop the program. This results in a stream.mp3 file I can then play back and listen to, and while this works, it isn't ideal.

Given the following code, how could I programmatically stop io::copy() early and have it save the file without killing the entire program?

extern crate reqwest;

use std::io;
use std::fs::File;

// Note that this is a direct link to the stream, not a webpage with HTML and a stream
const STREAM_URL: &str = "http://path.to/stream";

fn main() {
    let mut response = reqwest::get(STREAM_URL)
        .expect("Failed to request mp3 stream");
    let mut output = File::create("stream.mp3")
        .expect("Failed to create file!");
    io::copy(&mut response, &mut output)
        .expect("Failed to copy mp3 stream to file");
}

Solution

  • As the comments said, io::copy is a convenience function to read a Reader in full and write it's content to a Writer without stoping in between; it's used for when you do not care about intermediate state but just want the entire thing to be shipped from the reader to the writer.

    If you just want the first few Kb from the response, you can use io::Read::take, which limits the Reader to whatever limit you specified. It will return a new Reader which you can pass to io::copy.

    And yes, you can cut an MP3-file at arbitrary positions. It is a framed format and while you will most likely destroy the last frame, practically all mp3-decoders are able to handle this.


    Something to the tune of

    // `Read` needs to be in scope, so .take() is available on a `io::Read`
    use std::io::Read;
    use std::io;
    use std::fs::File;
    
    fn main() {
        let mut response = reqwest::get(STREAM_URL)
            .expect("Failed to request mp3 stream")
            .take(100*1024);  // Since `Response` is `Read`, we can `take` from it.
        let mut output = File::create("stream.mp3")
            .expect("Failed to create file!");
        // `response` is a limited reader, so it will only read 100kb before
        // returning EOF, signaling a successful completion to `copy`.
        io::copy(&mut response, &mut output)
            .expect("Failed to copy mp3 stream to file");
    }