forked from GNUsocial/gnu-social
[DOCS][Dev] Add Attachments
This commit is contained in:
parent
e8f57e8380
commit
47171069c2
@ -11,7 +11,7 @@
|
|||||||
- [Internationalization](./i18n.md)
|
- [Internationalization](./i18n.md)
|
||||||
- [Logging](./log.md)
|
- [Logging](./log.md)
|
||||||
- [Queue](./queue.md)
|
- [Queue](./queue.md)
|
||||||
- [Files](./files.md)
|
- [Attachments, Files and Thumbnails](./attachments.md)
|
||||||
- [Security](./security.md)
|
- [Security](./security.md)
|
||||||
- [HTTP](./http.md)
|
- [HTTP](./http.md)
|
||||||
- [Plugins](./plugins.md)
|
- [Plugins](./plugins.md)
|
||||||
|
112
docs/developer/src/attachments.md
Normal file
112
docs/developer/src/attachments.md
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
# Attachments, Files and Thumbnails
|
||||||
|
An attachment in GNU social can represent both file or an url.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
### Storage
|
||||||
|
Not every information should be stored in the database. Large blobs of data usually
|
||||||
|
find their space in storage. The two most common file abstractions you will find in
|
||||||
|
GNU social are `App\Util\TemporaryFile` and
|
||||||
|
`Symfony\Component\HttpFoundation\File\UploadedFile`.
|
||||||
|
|
||||||
|
The `UploadedFile` comes from Symfony and you'll find it when
|
||||||
|
working with forms that have file upload as inputs. The
|
||||||
|
`TemporaryFile` is how GNU social handles/represents any file that isn't
|
||||||
|
in a permanent state, i.e., not yet ready to be moved to storage.
|
||||||
|
|
||||||
|
So, as with the URL, the `Attachment` entity won't store
|
||||||
|
the information, only link to it.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
Here's how the `ImageEncoder` plugin creates a temporary file to manipulate an
|
||||||
|
image in a transaction fashion before committing its changes:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// TemporaryFile handles deleting the file if some error occurs
|
||||||
|
$temp = new TemporaryFile(['prefix' => 'image', 'suffix' => $extension]);
|
||||||
|
|
||||||
|
$image = Vips\Image::newFromFile($file->getRealPath(), ['access' => 'sequential']);
|
||||||
|
$width = Common::clamp($image->width, 0, Common::config('attachments', 'max_width'));
|
||||||
|
$height = Common::clamp($image->height, 0, Common::config('attachments', 'max_height'));
|
||||||
|
$image = $image->crop(0, 0, $width, $height);
|
||||||
|
$image->writeToFile($temp->getRealPath());
|
||||||
|
|
||||||
|
// Replace original file with the sanitized one
|
||||||
|
$temp->commit($file->getRealPath());
|
||||||
|
```
|
||||||
|
|
||||||
|
Note how we:
|
||||||
|
1. created a temporary file `$temp`,
|
||||||
|
2. then write the in-memory `$image` manipulation of `$file` to storage in `$temp`
|
||||||
|
3. and only then commit the changes in `$temp` to `$file`'s location.
|
||||||
|
|
||||||
|
If anything failed in 2 we would risk corrupting the input `$file`. In this case,
|
||||||
|
for performance's sake, most of the manipulation happens in memory. But it's
|
||||||
|
obvious that `TemporaryFile` can also be very useful for eventual in-storage
|
||||||
|
manipulations.
|
||||||
|
|
||||||
|
## Return a file via HTTP
|
||||||
|
Okay, it's fun that you can save files. But it isn't very
|
||||||
|
useful if you can't show the amazing changes or files you
|
||||||
|
generated to the client. For that, GNU social has
|
||||||
|
`App\Core\GSFile`.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```php
|
||||||
|
public function avatar_view(Request $request, int $gsactor_id)
|
||||||
|
{
|
||||||
|
$res = \Component\Avatar\Avatar::getAvatarFileInfo($gsactor_id);
|
||||||
|
return M::sendFile($res['file_path'], $res['mimetype'], $res['title']);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Simple enough.
|
||||||
|
|
||||||
|
### Storing a reference in database
|
||||||
|
Finally, you need a way to refer to previous files.
|
||||||
|
GNU social calls that representation of `App\Entity\Attachment`.
|
||||||
|
If a note refers to an `Attachment` then you can link them
|
||||||
|
using the entity `AttachmentToNote`.
|
||||||
|
|
||||||
|
> **Important:** Unless your plugin has its own file space,
|
||||||
|
> the core hashes the files and reuses `Attachment`s.
|
||||||
|
> Therefore, if you're deleting a file, you must ensure
|
||||||
|
> it is not being used somewhere you're not considering
|
||||||
|
> (like a note). You should only delete the file
|
||||||
|
> from storage when there are no notes linked with it in
|
||||||
|
> AttachmentToNote.
|
||||||
|
|
||||||
|
Call the functions `Attachment::validateAndStoreFileAsAttachment`
|
||||||
|
and `Attachment::validateAndStoreURLAsAttachment`.
|
||||||
|
|
||||||
|
## Thumbnails
|
||||||
|
|
||||||
|
Both files and urls can have an `AttachmentThumbnail`.
|
||||||
|
You can have an `AttachmentThumbnail` for every `Attachment`.
|
||||||
|
You can only have an `AttachmentThumbnail` if you have an
|
||||||
|
attachment first.
|
||||||
|
Read a plugin such as `ImageEncoder` to understand how thumbnails
|
||||||
|
can be generated from files. And `Embed` to understand how to generate
|
||||||
|
them from URLs.
|
||||||
|
|
||||||
|
The controller asking for them is the `App\Controller\Attachment::attachment_thumbnail` with
|
||||||
|
a call to `App\Entity\AttachmentThumbnail::getOrCreate()`.
|
||||||
|
|
||||||
|
## Trade-offs between decoupling and complexity
|
||||||
|
|
||||||
|
This kind of questions are deepened in our [wiki](https://agile.gnusocial.rocks/doku.php?id=attachment).
|
||||||
|
Despite that, in this case it is relevant enough to walk
|
||||||
|
a little through in the documentation. You'll note that
|
||||||
|
the Attachment entity has fairly specific fields such
|
||||||
|
as `remote_url` and `width`. Maybe for an Attachment
|
||||||
|
you could use the width field for the cover image of a
|
||||||
|
song, or not and just leave it null. And for a song
|
||||||
|
preview you could use width for duration and leave `height`
|
||||||
|
as null. The point is, we could have the entities
|
||||||
|
ImageAttachment and an ImageAttachmentThumbnail being
|
||||||
|
created by the ImageEncoder plugin and move these
|
||||||
|
specificities to the plugin. But the end code would
|
||||||
|
require far more database requests, become heavier,
|
||||||
|
and become harder to read. And maybe we're wasting a
|
||||||
|
bit more space (maybe!). but it's far from significant.
|
Loading…
Reference in New Issue
Block a user