File Attachments
Some operations take a file as a parameter: a user's avatar_file, a behavior's attachment_file, a bundle's
watermark_attachment_file, a site's logo_file, its icons and its login page background, and a style's file.
Give such a parameter a FileAttachment:
FileAttachment.FromFile(path)sends a local file, under its own name unless you give another.FileAttachment.FromBytes(bytes, fileName)sends bytes in memory. The array is not copied. Leave it unchanged until the operation ends.FileAttachment.FromStream(stream, fileName)sends the rest of a stream of yours, from its position when the operation starts.
Each also takes the file's media type, such as image/png; without one, it is sent as application/octet-stream.
The file name is sent exactly as given. Files.com still validates the file and its name as it does for any upload.
A model's file property, such as User.AvatarFile, takes a FileAttachment too, which SaveAsync sends with the
object's other changes.
How Files Are Sent
The files go in the same request as the operation's other parameters, and every other parameter keeps the exact
value it would have without a file: false, 0, empty text, null inside an object, empty lists and objects,
and numbers or booleans inside an object alike.
- When form fields can represent every value exactly, and the file names unchanged, the request is
multipart/form-data, with each file as a part of its own. - Otherwise the request is JSON, in which each file is the API's file object, with its bytes as Base64 in
encoded_content, its name infilenameand its media type intype. The request is then about a third larger than the files. That is the case for an empty list or object,nullor a number or boolean inside an object, a list of objects, and a file name with%,\,/or", which a form would change.
Either way, files are read in chunks as the request is sent, and never held whole in memory for it, and the request
states its exact length. A large request is timed as an upload is: WriteTimeout limits how long it may go without
sending any data, however long it takes in all.
A file parameter also takes the API's file object itself, a Dictionary<string, object> with encoded_content,
filename and type, which is sent as it is. A FileAttachment outside a file parameter, including one nested in another parameter's dictionary, throws InvalidParameterException before the request is sent.
Retries, Redirects and Failures
A request that is sent again, after a failure that may be temporary, a redirect that repeats it, or a move to the site's region, sends the same bytes. A request with files that can change something, such as an update, is sent again only when it certainly was not carried out, such as after a move to the site's region: after a lost answer, or an error such as 503, it is not, and says it may have taken effect (see Maximum Network Retries under Configuration). So files and streams must keep their contents and length until the operation ends:
- A local file is opened when the operation starts and read through that one handle for every attempt. A file whose
length changes fails the operation with an
IOExceptionbefore it is sent again. - A stream that can seek is read in place, from its starting position, for each attempt, and must keep its length.
- A stream that cannot seek is first copied, up to
maxBytes(64 MiB unless you choose otherwise), to a temporary file of the operation's own, readable only by you where the system allows. A longer stream fails the operation withInvalidParameterExceptionbefore anything is sent.
A failure to read a file or stream, such as an IOException, is thrown as it is, and the request is not sent again,
even when retries remain. A file that cannot be opened fails the same way, before anything is sent, with .NET's own
exception, such as System.IO.FileNotFoundException. FilesCom.FileNotFoundException represents the API error. If both namespaces are in scope, use the fully qualified exception name in your catch clause, or catch IOException for local file failures.
The operation closes files it opened and deletes its temporary files when it ends, whether it succeeds or fails. A deletion failure is logged and does not replace the operation's result or exception. Streams you provide remain open. Their final position is determined by the SDK.
The SDK's DEBUG log shows the parameters of a request with files, with each file's length in place of its bytes, which are never logged.