2016-09-12 08:51:02 +00:00
|
|
|
use std::fs::File;
|
|
|
|
use std::path::{Path, PathBuf};
|
2017-11-27 22:58:50 +00:00
|
|
|
use std::io;
|
2016-09-12 09:43:34 +00:00
|
|
|
use std::ops::{Deref, DerefMut};
|
2016-09-12 08:51:02 +00:00
|
|
|
|
2019-06-13 01:48:02 +00:00
|
|
|
use crate::request::Request;
|
|
|
|
use crate::response::{self, Responder};
|
|
|
|
use crate::http::ContentType;
|
2016-10-04 00:09:13 +00:00
|
|
|
|
2016-11-03 16:05:41 +00:00
|
|
|
/// A file with an associated name; responds with the Content-Type based on the
|
|
|
|
/// file extension.
|
2016-09-12 09:43:34 +00:00
|
|
|
#[derive(Debug)]
|
2016-09-12 08:51:02 +00:00
|
|
|
pub struct NamedFile(PathBuf, File);
|
|
|
|
|
|
|
|
impl NamedFile {
|
2016-11-03 16:05:41 +00:00
|
|
|
/// Attempts to open a file in read-only mode.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
///
|
|
|
|
/// This function will return an error if path does not already exist. Other
|
|
|
|
/// errors may also be returned according to
|
2018-10-06 13:25:17 +00:00
|
|
|
/// [`OpenOptions::open()`](std::fs::OpenOptions::open()).
|
2016-11-03 16:05:41 +00:00
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use rocket::response::NamedFile;
|
|
|
|
///
|
2017-02-02 10:16:21 +00:00
|
|
|
/// # #[allow(unused_variables)]
|
2016-11-03 16:05:41 +00:00
|
|
|
/// let file = NamedFile::open("foo.txt");
|
|
|
|
/// ```
|
2016-09-12 08:51:02 +00:00
|
|
|
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<NamedFile> {
|
|
|
|
let file = File::open(path.as_ref())?;
|
|
|
|
Ok(NamedFile(path.as_ref().to_path_buf(), file))
|
|
|
|
}
|
|
|
|
|
2016-11-03 16:05:41 +00:00
|
|
|
/// Retrieve the underlying `File`.
|
2016-09-25 11:07:03 +00:00
|
|
|
#[inline(always)]
|
|
|
|
pub fn file(&self) -> &File {
|
|
|
|
&self.1
|
|
|
|
}
|
|
|
|
|
2016-12-23 19:51:11 +00:00
|
|
|
/// Take the underlying `File`.
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn take_file(self) -> File {
|
|
|
|
self.1
|
|
|
|
}
|
|
|
|
|
2016-11-03 16:05:41 +00:00
|
|
|
/// Retrieve a mutable borrow to the underlying `File`.
|
2016-09-25 11:07:03 +00:00
|
|
|
#[inline(always)]
|
|
|
|
pub fn file_mut(&mut self) -> &mut File {
|
|
|
|
&mut self.1
|
|
|
|
}
|
|
|
|
|
2016-11-03 16:05:41 +00:00
|
|
|
/// Retrieve the path of this file.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # use std::io;
|
|
|
|
/// use rocket::response::NamedFile;
|
|
|
|
///
|
2017-02-02 10:16:21 +00:00
|
|
|
/// # #[allow(dead_code)]
|
2016-11-03 16:05:41 +00:00
|
|
|
/// # fn demo_path() -> io::Result<()> {
|
|
|
|
/// let file = NamedFile::open("foo.txt")?;
|
|
|
|
/// assert_eq!(file.path().as_os_str(), "foo.txt");
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2016-09-25 11:07:03 +00:00
|
|
|
#[inline(always)]
|
2016-09-12 08:51:02 +00:00
|
|
|
pub fn path(&self) -> &Path {
|
|
|
|
self.0.as_path()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-03 16:05:41 +00:00
|
|
|
/// Streams the named file to the client. Sets or overrides the Content-Type in
|
|
|
|
/// the response according to the file's extension if the extension is
|
2018-10-06 13:25:17 +00:00
|
|
|
/// recognized. See [`ContentType::from_extension()`] for more information. If
|
|
|
|
/// you would like to stream a file with a different Content-Type than that
|
|
|
|
/// implied by its extension, use a [`File`] directly.
|
2019-07-24 15:21:52 +00:00
|
|
|
impl<'r> Responder<'r> for NamedFile {
|
|
|
|
fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> {
|
|
|
|
Box::pin(async move {
|
|
|
|
let mut response = self.1.respond_to(req).await?;
|
|
|
|
if let Some(ext) = self.0.extension() {
|
|
|
|
if let Some(ct) = ContentType::from_extension(&ext.to_string_lossy()) {
|
|
|
|
response.set_header(ct);
|
|
|
|
}
|
2016-09-12 08:51:02 +00:00
|
|
|
}
|
|
|
|
|
2019-07-24 15:21:52 +00:00
|
|
|
Ok(response)
|
|
|
|
})
|
2016-09-12 08:51:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-12 09:43:34 +00:00
|
|
|
impl Deref for NamedFile {
|
|
|
|
type Target = File;
|
|
|
|
|
|
|
|
fn deref(&self) -> &File {
|
|
|
|
&self.1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DerefMut for NamedFile {
|
|
|
|
fn deref_mut(&mut self) -> &mut File {
|
|
|
|
&mut self.1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-25 11:07:03 +00:00
|
|
|
impl io::Read for NamedFile {
|
|
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
|
|
self.file().read(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
|
|
|
self.file().read_to_end(buf)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl io::Write for NamedFile {
|
|
|
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
|
|
self.file().write(buf)
|
|
|
|
}
|
|
|
|
|
2016-09-30 22:20:11 +00:00
|
|
|
fn flush(&mut self) -> io::Result<()> {
|
|
|
|
self.file().flush()
|
|
|
|
}
|
2016-09-25 11:07:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl io::Seek for NamedFile {
|
|
|
|
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
|
|
|
self.file().seek(pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-13 01:48:02 +00:00
|
|
|
impl io::Read for &NamedFile {
|
2016-09-25 11:07:03 +00:00
|
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
|
|
self.file().read(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
|
|
|
self.file().read_to_end(buf)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-13 01:48:02 +00:00
|
|
|
impl io::Write for &NamedFile {
|
2016-09-25 11:07:03 +00:00
|
|
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
|
|
self.file().write(buf)
|
|
|
|
}
|
|
|
|
|
2016-09-30 22:20:11 +00:00
|
|
|
fn flush(&mut self) -> io::Result<()> {
|
|
|
|
self.file().flush()
|
|
|
|
}
|
2016-09-25 11:07:03 +00:00
|
|
|
}
|
|
|
|
|
2019-06-13 01:48:02 +00:00
|
|
|
impl io::Seek for &NamedFile {
|
2016-09-25 11:07:03 +00:00
|
|
|
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
|
|
|
self.file().seek(pos)
|
|
|
|
}
|
|
|
|
}
|