pub struct SaveBuilder<S> { /* private fields */ }
Expand description
A builder for saving a file or files to the local filesystem.
§OpenOptions
This builder holds an instance of std::fs::OpenOptions
which is used
when creating the new file(s).
By default, the open options are set with .write(true).create_new(true)
,
so if the file already exists then an error will be thrown. This is to avoid accidentally
overwriting files from other requests.
If you want to modify the options used to open the save file, you can use
mod_open_opts()
.
§File Size and Count Limits
You can set a size limit for individual fields with size_limit()
, which takes either u64
or Option<u64>
.
You can also set the maximum number of fields to process with count_limit()
, which
takes either u32
or Option<u32>
. This only has an effect when using
SaveBuilder<[&mut] Multipart>
.
By default, these limits are set conservatively to limit the maximum memory and disk space
usage of a single request. You should set count_limit
specifically for each request endpoint
based on the number of fields you’re expecting (exactly to that number if you’re not expecting
duplicate fields).
§Memory Threshold and Text Policy
By default, small fields (a few kilobytes or smaller) will be read directly to memory
without creating a file. This behavior is controlled by the memory_threshold()
setter. You can
roughly tune the maximum memory a single request uses by tuning
count_limit * memory_threshold
If a field appears to contain text data (its content-type is text/*
or it doesn’t declare
one), SaveBuilder
can read it to a string instead of saving the raw bytes as long as it falls
below the set memory_threshold
.
By default, the behavior is to attempt to validate the data as UTF-8, falling back to saving
just the bytes if the validation fails at any point. You can restore/ensure this behavior
with the try_text()
modifier.
Alternatively, you can use the force_text()
modifier to make the save operation return
an error when UTF-8 decoding fails, though this only holds true while the size is below
memory_threshold
. The ignore_text()
modifier turns off UTF-8 validation altogether.
UTF-8 validation is performed incrementally (after every BufRead::fill_buf()
call)
to hopefully maximize throughput, instead of blocking while the field is read to completion
and performing validation over the entire result at the end. (RFC: this could be a lot of
unnecessary work if most fields end up being written to the filesystem, however, but this
can be turned off with ignore_text()
if it fits the use-case.)
§Warning: Do not trust user input!
It is a serious security risk to create files or directories with paths based on user input. A malicious user could craft a path which can be used to overwrite important files, such as web templates, static assets, Javascript files, database files, configuration files, etc., if they are writable by the server process.
This can be mitigated somewhat by setting filesystem permissions as conservatively as possible and running the server under its own user with restricted permissions, but you should still not use user input directly as filesystem paths. If it is truly necessary, you should sanitize user input such that it cannot cause a path to be misinterpreted by the OS. Such functionality is outside the scope of this crate.
Implementations§
Source§impl<S> SaveBuilder<S>
impl<S> SaveBuilder<S>
Common methods for whole requests as well as individual fields.
Sourcepub fn size_limit<L: Into<Option<u64>>>(self, limit: L) -> Self
pub fn size_limit<L: Into<Option<u64>>>(self, limit: L) -> Self
Set the maximum number of bytes to write out per file.
Can be u64
or Option<u64>
. If None
or u64::MAX
, clears the limit.
Sourcepub fn mod_open_opts<F: FnOnce(&mut OpenOptions)>(self, opts_fn: F) -> Self
pub fn mod_open_opts<F: FnOnce(&mut OpenOptions)>(self, opts_fn: F) -> Self
Modify the OpenOptions
used to open any files for writing.
The write
flag will be reset to true
after the closure returns. (It’d be pretty
pointless otherwise, right?)
Sourcepub fn memory_threshold(self, memory_threshold: u64) -> Self
pub fn memory_threshold(self, memory_threshold: u64) -> Self
Set the threshold at which to switch from copying a field into memory to copying it to disk.
If 0
, forces fields to save directly to the filesystem.
If u64::MAX
, effectively forces fields to always save to memory.
Sourcepub fn try_text(self) -> Self
pub fn try_text(self) -> Self
When encountering a field that is apparently text, try to read it to a string or fall back to binary otherwise.
If set for an individual field (SaveBuilder<&mut MultipartData<_>>
), will
always attempt to decode text regardless of the field’s Content-Type
.
Has no effect once memory_threshold
has been reached.
Sourcepub fn force_text(self) -> Self
pub fn force_text(self) -> Self
When encountering a field that is apparently text, read it to a string or return an error.
If set for an individual field (SaveBuilder<&mut MultipartData<_>>
), will
always attempt to decode text regardless of the field’s Content-Type
.
(RFC: should this continue to validate UTF-8 when writing to the filesystem?)
Sourcepub fn ignore_text(self) -> Self
pub fn ignore_text(self) -> Self
Don’t try to read or validate any field data as UTF-8.
Source§impl<M> SaveBuilder<M>where
M: ReadEntry,
impl<M> SaveBuilder<M>where
M: ReadEntry,
Save API for whole multipart requests.
Sourcepub fn count_limit<L: Into<Option<u32>>>(self, count_limit: L) -> Self
pub fn count_limit<L: Into<Option<u32>>>(self, count_limit: L) -> Self
Set the maximum number of fields to process.
Can be u32
or Option<u32>
. If None
or u32::MAX
, clears the limit.
Sourcepub fn temp(self) -> EntriesSaveResult<M>
pub fn temp(self) -> EntriesSaveResult<M>
Save all fields in the request using a new temporary directory prefixed with
multipart-rs
in the OS temporary directory.
For more options, create a TempDir
yourself and pass it to with_temp_dir()
instead.
See with_entries()
for more info.
§Note: Temporary
See SaveDir
for more info (the type of Entries::save_dir
).
Sourcepub fn temp_with_prefix(self, prefix: &str) -> EntriesSaveResult<M>
pub fn temp_with_prefix(self, prefix: &str) -> EntriesSaveResult<M>
Save all fields in the request using a new temporary directory with the given string as a prefix in the OS temporary directory.
For more options, create a TempDir
yourself and pass it to with_temp_dir()
instead.
See with_entries()
for more info.
§Note: Temporary
See SaveDir
for more info (the type of Entries::save_dir
).
Sourcepub fn with_temp_dir(self, tempdir: TempDir) -> EntriesSaveResult<M>
pub fn with_temp_dir(self, tempdir: TempDir) -> EntriesSaveResult<M>
Save all fields in the request using the given TempDir
.
See with_entries()
for more info.
The TempDir
is returned in the result under Entries::save_dir
.
Sourcepub fn with_dir<P: Into<PathBuf>>(self, dir: P) -> EntriesSaveResult<M>
pub fn with_dir<P: Into<PathBuf>>(self, dir: P) -> EntriesSaveResult<M>
Save the file fields in the request to a new permanent directory with the given path.
Any nonexistent directories in the path will be created.
See with_entries()
for more info.
Sourcepub fn with_entries(self, entries: Entries) -> EntriesSaveResult<M>
pub fn with_entries(self, entries: Entries) -> EntriesSaveResult<M>
Commence the save operation using the existing Entries
instance.
May be used to resume a saving operation after handling an error.
If count_limit
is set, only reads that many fields before returning an error.
If you wish to resume from PartialReason::CountLimit
, simply remove some entries.
Note that PartialReason::CountLimit
will still be returned if the number of fields
reaches u32::MAX
, but this would be an extremely degenerate case.
Source§impl<'m, M: 'm> SaveBuilder<&'m mut MultipartData<M>>where
MultipartData<M>: BufRead,
impl<'m, M: 'm> SaveBuilder<&'m mut MultipartData<M>>where
MultipartData<M>: BufRead,
Save API for individual fields.
Sourcepub fn temp(&mut self) -> FieldSaveResult
pub fn temp(&mut self) -> FieldSaveResult
Save the field data, potentially using a file with a random name in the OS temporary directory.
See with_path()
for more details.
Sourcepub fn with_filename(&mut self, filename: &str) -> FieldSaveResult
pub fn with_filename(&mut self, filename: &str) -> FieldSaveResult
Save the field data, potentially using a file with the given name in the OS temporary directory.
See with_path()
for more details.
Sourcepub fn with_dir<P: AsRef<Path>>(&mut self, dir: P) -> FieldSaveResult
pub fn with_dir<P: AsRef<Path>>(&mut self, dir: P) -> FieldSaveResult
Save the field data, potentially using a file with a random alphanumeric name in the given directory.
See with_path()
for more details.
Sourcepub fn with_path<P: Into<PathBuf>>(&mut self, path: P) -> FieldSaveResult
pub fn with_path<P: Into<PathBuf>>(&mut self, path: P) -> FieldSaveResult
Save the field data, potentially using a file with the given path.
Creates any missing directories in the path (RFC: skip this step?).
Uses the contained OpenOptions
to create the file.
Truncates the file to the given size_limit
, if set.
The no directories or files will be created until the set memory_threshold
is reached.
If size_limit
is set and less than or equal to memory_threshold
,
then the disk will never be touched.
Sourcepub fn write_to<W: Write>(&mut self, dest: W) -> SaveResult<u64, u64>
pub fn write_to<W: Write>(&mut self, dest: W) -> SaveResult<u64, u64>
Write out the field data to dest
, truncating if a limit was set.
Returns the number of bytes copied, and whether or not the limit was reached
(tested by MultipartFile::fill_buf().is_empty()
so no bytes are consumed).
Retries on interrupts.