109 lines
4.0 KiB
Markdown
109 lines
4.0 KiB
Markdown
|
# Uploading Files
|
||
|
|
||
|
To make files work as expected, there's a lot going on behind the scenes. Make
|
||
|
sure to read through the [Files](../getting-started/files.md) section in
|
||
|
Getting Started first as we'll be building on that information.
|
||
|
|
||
|
This section only talks about file uploading. For non-uploaded files such as
|
||
|
URLs and file IDs, you just need to pass a string.
|
||
|
|
||
|
## Fields
|
||
|
|
||
|
Let's start by talking about how the library represents files as part of a
|
||
|
Config.
|
||
|
|
||
|
### Static Fields
|
||
|
|
||
|
Most endpoints use static file fields. For example, `sendPhoto` expects a single
|
||
|
file named `photo`. All we have to do is set that single field with the correct
|
||
|
value (either a string or multipart file). Methods like `sendDocument` take two
|
||
|
file uploads, a `document` and a `thumb`. These are pretty straightforward.
|
||
|
|
||
|
Remembering that the `Fileable` interface only requires one method, let's
|
||
|
implement it for `DocumentConfig`.
|
||
|
|
||
|
```go
|
||
|
func (config DocumentConfig) files() []RequestFile {
|
||
|
// We can have multiple files, so we'll create an array. We also know that
|
||
|
// there always is a document file, so initialize the array with that.
|
||
|
files := []RequestFile{{
|
||
|
Name: "document",
|
||
|
File: config.File,
|
||
|
}}
|
||
|
|
||
|
// We'll only add a file if we have one.
|
||
|
if config.Thumb != nil {
|
||
|
files = append(files, RequestFile{
|
||
|
Name: "thumb",
|
||
|
File: config.Thumb,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return files
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Telegram also supports the `attach://` syntax (discussed more later) for
|
||
|
thumbnails, but there's no reason to make things more complicated.
|
||
|
|
||
|
### Dynamic Fields
|
||
|
|
||
|
Of course, not everything can be so simple. Methods like `sendMediaGroup`
|
||
|
can accept many files, and each file can have custom markup. Using a static
|
||
|
field isn't possible because we need to specify which field is attached to each
|
||
|
item. Telegram introduced the `attach://` syntax for this.
|
||
|
|
||
|
Let's follow through creating a new media group with string and file uploads.
|
||
|
|
||
|
First, we start by creating some `InputMediaPhoto`.
|
||
|
|
||
|
```go
|
||
|
photo := tgbotapi.NewInputMediaPhoto("tests/image.jpg")
|
||
|
url := tgbotapi.NewInputMediaPhoto(tgbotapi.FileURL("https://i.imgur.com/unQLJIb.jpg"))
|
||
|
```
|
||
|
|
||
|
This created a new `InputMediaPhoto` struct, with a type of `photo` and the
|
||
|
media interface that we specified.
|
||
|
|
||
|
We'll now create our media group with the photo and URL.
|
||
|
|
||
|
```go
|
||
|
mediaGroup := NewMediaGroup(ChatID, []interface{}{
|
||
|
photo,
|
||
|
url,
|
||
|
})
|
||
|
```
|
||
|
|
||
|
A `MediaGroupConfig` stores all of the media in an array of interfaces. We now
|
||
|
have all of the data we need to upload, but how do we figure out field names for
|
||
|
uploads? We didn't specify `attach://unique-file` anywhere.
|
||
|
|
||
|
When the library goes to upload the files, it looks at the `params` and `files`
|
||
|
for the Config. The params are generated by transforming the file into a value
|
||
|
more suitable for uploading, file IDs and URLs are untouched but uploaded types
|
||
|
are all changed into `attach://file-%d`. When collecting a list of files to
|
||
|
upload, it names them the same way. This creates a nearly transparent way of
|
||
|
handling multiple files in the background without the user having to consider
|
||
|
what's going on.
|
||
|
|
||
|
## Library Processing
|
||
|
|
||
|
If at some point in the future new upload types are required, let's talk about
|
||
|
where the current types are used.
|
||
|
|
||
|
Upload types are defined in `configs.go`. Where possible, type aliases are
|
||
|
preferred. Structs can be used when multiple fields are required.
|
||
|
|
||
|
The main usage of the upload types happens in `UploadFiles`. It switches on each
|
||
|
file's type in order to determine how to upload it. Files that aren't uploaded
|
||
|
(file IDs, URLs) are converted back into strings and passed through as strings
|
||
|
into the correct field. Uploaded types are processed as needed (opening files,
|
||
|
etc.) and written into the form using a copy approach in a goroutine to reduce
|
||
|
memory usage.
|
||
|
|
||
|
In addition to `UploadFiles`, there's more processing of upload types in the
|
||
|
`prepareInputMediaParam` and `prepareInputMediaFile` functions. These look at
|
||
|
the `InputMedia` types to determine which files are uploaded and which are
|
||
|
passed through as strings. They only need to be aware of which files need to be
|
||
|
replaced with `attach://` fields.
|