pub enum TempFile<'v> {
// some variants omitted
}
Expand description
A data and form guard that streams data into a temporary file.
TempFile
is a data and form field (both value and data fields) guard that
streams incoming data into file in a temporary location. The file is deleted
when the TempFile
handle is dropped unless it is persisted with
TempFile::persist_to()
or copied with TempFile::copy_to()
.
Hazards
Temporary files are cleaned by system file cleaners periodically. While an
attempt is made not to delete temporary files in use, detection of when a
temporary file is being used is unreliable. As a result, a time-of-check to
time-of-use race condition from the creation of a TempFile
to the
persistence of the TempFile
may occur. Specifically, the following
sequence may occur:
- A
TempFile
is created at random pathfoo
. - The system cleaner removes the file at path
foo
. - Another application creates a file at path
foo
. - The
TempFile
, ostensibly at pathfoo
, is persisted unexpectedly with contents different from those in step 1.
To safe-guard against this issue, you should ensure that your temporary file cleaner, if any, does not delete files too eagerly.
Configuration
TempFile
is configured via the following config
parameters:
Name | Default | Description |
---|---|---|
temp_dir | env::temp_dir() | Directory for temporary file storage. |
limits.file | 1MiB | Default limit for all file extensions. |
limits.file/$ext | N/A | Limit for files with extension $ext . |
When used as a form guard, the extension $ext
is identified by the form
field’s Content-Type
(ContentType::extension()
). When used as a data
guard, the extension is identified by the Content-Type of the request, if
any. If there is no Content-Type, the limit file
is used.
Cappable
A data stream can be partially read into a TempFile
even if the incoming
stream exceeds the data limit via the Capped<TempFile>
data and form
guard.
Examples
Data Guard
use rocket::fs::TempFile;
#[post("/upload", data = "<file>")]
async fn upload(mut file: TempFile<'_>) -> std::io::Result<()> {
file.persist_to("/tmp/complete/file.txt").await?;
Ok(())
}
Form Field
use rocket::fs::TempFile;
use rocket::form::Form;
#[derive(FromForm)]
struct Upload<'f> {
upload: TempFile<'f>
}
#[post("/form", data = "<form>")]
async fn upload(mut form: Form<Upload<'_>>) -> std::io::Result<()> {
form.upload.persist_to("/tmp/complete/file.txt").await?;
Ok(())
}
See also the Capped
documentation for an example of Capped<TempFile>
as a data guard.
Implementations§
source§impl<'v> TempFile<'v>
impl<'v> TempFile<'v>
sourcepub async fn persist_to<P>(&mut self, path: P) -> Result<()>where
P: AsRef<Path>,
pub async fn persist_to<P>(&mut self, path: P) -> Result<()>where P: AsRef<Path>,
Persists the temporary file, moving it to path
. If a file exists at
the target path, self
will atomically replace it. self.path()
is
updated to path
.
This method does not create a copy of self
, nor a new link to the
contents of self
: it renames the temporary file to path
and marks it
as non-temporary. As a result, this method cannot be used to create
multiple copies of self
. To create multiple links, use
std::fs::hard_link()
with path
as the src
after calling this
method.
Cross-Device Persistence
Attempting to persist a temporary file across logical devices (or mount points) will result in an error. This is a limitation of the underlying OS. Your options are thus:
-
Store temporary file in the same logical device.
Change the
temp_dir
configuration parameter to be in the same logical device as the permanent location. This is the preferred solution. -
Copy the temporary file using
TempFile::copy_to()
orTempFile::move_copy_to()
instead.This is a full copy of the file, creating a duplicate version of the file at the destination. This should be avoided for performance reasons.
Example
use rocket::fs::TempFile;
#[post("/", data = "<file>")]
async fn handle(mut file: TempFile<'_>) -> std::io::Result<()> {
file.persist_to(&some_path).await?;
assert_eq!(file.path(), Some(&*some_path));
Ok(())
}
sourcepub async fn copy_to<P>(&mut self, path: P) -> Result<()>where
P: AsRef<Path>,
pub async fn copy_to<P>(&mut self, path: P) -> Result<()>where P: AsRef<Path>,
Persists the temporary file at its temporary path and creates a full
copy at path
. The self.path()
is not updated, unless no temporary
file existed prior, and the temporary file is not removed. Thus, there
will be two files with the same contents.
Unlike TempFile::persist_to()
, this method does not incur
cross-device limitations, at the performance cost of a full copy. Prefer
to use persist_to()
with a valid temp_dir
configuration parameter if
no more than one copy of a file is required.
Example
use rocket::fs::TempFile;
#[post("/", data = "<file>")]
async fn handle(mut file: TempFile<'_>) -> std::io::Result<()> {
file.copy_to(&some_path).await?;
file.copy_to(&some_other_path).await?;
assert_eq!(file.path(), Some(&*some_path));
Ok(())
}
sourcepub async fn move_copy_to<P>(&mut self, path: P) -> Result<()>where
P: AsRef<Path>,
pub async fn move_copy_to<P>(&mut self, path: P) -> Result<()>where P: AsRef<Path>,
Persists the temporary file at its temporary path, creates a full copy
at path
, and then deletes the temporary file. self.path()
is updated
to path
.
Like TempFile::copy_to()
and unlike TempFile::persist_to()
, this
method does not incur cross-device limitations, at the performance cost
of a full copy and file deletion. Prefer to use persist_to()
with a
valid temp_dir
configuration parameter if no more than one copy of a
file is required.
Example
use rocket::fs::TempFile;
#[post("/", data = "<file>")]
async fn handle(mut file: TempFile<'_>) -> std::io::Result<()> {
file.move_copy_to(&some_path).await?;
Ok(())
}
sourcepub fn len(&self) -> u64
pub fn len(&self) -> u64
Returns the size, in bytes, of the file.
This method does not perform any system calls.
use rocket::fs::TempFile;
#[post("/", data = "<file>")]
fn handler(file: TempFile<'_>) {
let file_len = file.len();
}
sourcepub fn path(&self) -> Option<&Path>
pub fn path(&self) -> Option<&Path>
Returns the path to the file if it is known.
Once a file is persisted with TempFile::persist_to()
, this method is
guaranteed to return Some
. Prior to this point, however, this method
may return Some
or None
, depending on whether the file is on disk or
partially buffered in memory.
use rocket::fs::TempFile;
#[post("/", data = "<file>")]
async fn handle(mut file: TempFile<'_>) -> std::io::Result<()> {
file.persist_to(&some_path).await?;
assert_eq!(file.path(), Some(&*some_path));
Ok(())
}
sourcepub fn name(&self) -> Option<&str>
pub fn name(&self) -> Option<&str>
Returns the sanitized file name as specified in the form field.
A multipart data form field can optionally specify the name of a file. A browser will typically send the actual name of a user’s selected file in this field, but clients are also able to specify any name, including invalid or dangerous file names. This method returns a sanitized version of that value, if it was specified, suitable and safe for use as a permanent file name.
Note that you will likely want to prepend or append random or user-specific components to the name to avoid collisions; UUIDs make for a good “random” data.
See FileName::as_str()
for specifics on sanitization.
use rocket::fs::TempFile;
#[post("/", data = "<file>")]
async fn handle(mut file: TempFile<'_>) -> std::io::Result<()> {
if let Some(name) = file.name() {
// Because of Rocket's sanitization, this is safe.
file.persist_to(&some_dir.join(name)).await?;
}
Ok(())
}
sourcepub fn raw_name(&self) -> Option<&FileName>
pub fn raw_name(&self) -> Option<&FileName>
Returns the raw name of the file as specified in the form field.
use rocket::fs::TempFile;
#[post("/", data = "<file>")]
async fn handle(mut file: TempFile<'_>) {
let raw_name = file.raw_name();
}
sourcepub fn content_type(&self) -> Option<&ContentType>
pub fn content_type(&self) -> Option<&ContentType>
Returns the Content-Type of the file as specified in the form field.
A multipart data form field can optionally specify the content-type of a file. A browser will typically sniff the file’s extension to set the content-type. This method returns that value, if it was specified.
use rocket::fs::TempFile;
#[post("/", data = "<file>")]
fn handle(file: TempFile<'_>) {
let content_type = file.content_type();
}
Trait Implementations§
source§impl<'r> FromData<'r> for TempFile<'_>
impl<'r> FromData<'r> for TempFile<'_>
§type Error = <Capped<TempFile<'_>> as FromData<'r>>::Error
type Error = <Capped<TempFile<'_>> as FromData<'r>>::Error
source§fn from_data<'life0, 'async_trait>(
r: &'r Request<'life0>,
d: Data<'r>
) -> Pin<Box<dyn Future<Output = Outcome<'r, Self>> + Send + 'async_trait>>where
Self: 'async_trait,
'r: 'async_trait,
'life0: 'async_trait,
fn from_data<'life0, 'async_trait>( r: &'r Request<'life0>, d: Data<'r> ) -> Pin<Box<dyn Future<Output = Outcome<'r, Self>> + Send + 'async_trait>>where Self: 'async_trait, 'r: 'async_trait, 'life0: 'async_trait,
Self
from the incoming request body data. Read moresource§impl<'v> FromFormField<'v> for TempFile<'v>
impl<'v> FromFormField<'v> for TempFile<'v>
Auto Trait Implementations§
impl<'v> RefUnwindSafe for TempFile<'v>
impl<'v> Send for TempFile<'v>
impl<'v> Sync for TempFile<'v>
impl<'v> Unpin for TempFile<'v>
impl<'v> UnwindSafe for TempFile<'v>
Blanket Implementations§
§impl<'a, T> AsTaggedExplicit<'a> for Twhere
T: 'a,
impl<'a, T> AsTaggedExplicit<'a> for Twhere T: 'a,
§impl<'a, T> AsTaggedImplicit<'a> for Twhere
T: 'a,
impl<'a, T> AsTaggedImplicit<'a> for Twhere T: 'a,
source§impl<T> Instrument for T
impl<T> Instrument for T
source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
§impl<T> IntoCollection<T> for T
impl<T> IntoCollection<T> for T
§fn into_collection<A>(self) -> SmallVec<A>where
A: Array<Item = T>,
fn into_collection<A>(self) -> SmallVec<A>where A: Array<Item = T>,
self
into a collection.