# File-uploader-crystal: A simple file uploader made on crystal using Kemal

**URL:** <https://forum.crystal-lang.org/t/file-uploader-crystal-a-simple-file-uploader-made-on-crystal-using-kemal/7058>\
**Category:** Community\
**Created:** [August 5, 2024, 10:08pm UTC](https://forum.crystal-lang.org/t/file-uploader-crystal-a-simple-file-uploader-made-on-crystal-using-kemal/7058 "2024-08-05T22:08:50Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Fijxu](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fijxu/32/3422_2.png) [@Fijxu](https://forum.crystal-lang.org/u/Fijxu)\
**Post date:** [August 5, 2024, 10:08pm UTC](https://forum.crystal-lang.org/t/file-uploader-crystal-a-simple-file-uploader-made-on-crystal-using-kemal/7058/1 "2024-08-05T22:08:50Z")

</div>

Hi. I did an [uguu](https://github.com/nokonoko/uguu) replacement on Crystal. Is pretty straight forward. A simple server where you can upload any media temporarily or permanently (depending on server configuration).

It supports:

- Random filenames with custom length
- Files can be retrieved with or without the extension
- Unix sockets (Although I need to fix the socket permissions in some way or NGINX or any other reverse proxy is not able to access to the server)
- Deletion link

Special thanks to:

- The [invidious](https://github.com/iv-org/invidious) codebase
- All the discussion in the “[Is this a good way to generate a random string?](https://forum.crystal-lang.org/t/is-this-a-good-way-to-generate-a-random-string/6986)” forum post
- zw963 for creating [GitHub - crystal-china/base58.cr: Generate random strings with Base58 alphabet, with better performance.](https://github.com/crystal-china/base58.cr) based on the forum discussion mentioned above
- The awesome crystal documentation (I really like it)

Most of the development was made in less than a week so if you find a bug, just tell me. Feel free to break it too. Suggestions and recommendations are appreciated.

> **[file-uploader-crystal](https://codeberg.org/Fijxu/file-uploader-crystal)**
>
> File uploader that I did in a few days xdxd

The front-end could be better but I don’t like front-end development at all so I just asked ChatGPT to make it lol.

---

<div class="post-metadata">

**Author:** ![jwoertink](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/jwoertink/32/50_2.png) [@jwoertink](https://forum.crystal-lang.org/u/jwoertink)\
**Post date:** [August 6, 2024, 4:17pm UTC](https://forum.crystal-lang.org/t/file-uploader-crystal-a-simple-file-uploader-made-on-crystal-using-kemal/7058/2 "2024-08-06T16:17:07Z")

</div>

Nice work!

Side note, It’s funny how many base58 shards there are 😂

> **[GitHub - crystal-china/base58.cr: Generate random strings with Base58 alphabet,...](https://github.com/crystal-china/base58.cr)**
>
> Generate random strings with Base58 alphabet, with better performance.

> **[GitHub - valenciaj/crystal-base58: Generate random strings with Base58 alphabet](https://github.com/valenciaj/crystal-base58)**
>
> Generate random strings with Base58 alphabet

> **[GitHub - wyhaines/base58.cr: This is a performance optimized implementation of...](https://github.com/wyhaines/base58.cr)**
>
> This is a performance optimized implementation of base58 binary-to-text encoding algorithm.

> **[GitHub - wout/base\_x: A Crystal shard for base encoding / decoding of...](https://github.com/wout/base_x)**
>
> A Crystal shard for base encoding / decoding of any given alphabet with optional bitcoin-style leading zero compression.

> **[GitHub - radbas/multibase.cr: Base transcoder (Base62, Base58, BaseN) with...](https://github.com/radbas/multibase.cr)**
>
> Base transcoder (Base62, Base58, BaseN) with multibyte character alphabet support.

> **[GitHub - russ/base58: base58 for Crystal](https://github.com/russ/base58)**
>
> base58 for Crystal

And these are just the ones that I know of lol

---

<div class="post-metadata">

**Author:** ![sdogruyol](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/sdogruyol/32/38_2.png) [@sdogruyol](https://forum.crystal-lang.org/u/sdogruyol)\
**Post date:** [August 6, 2024, 7:18pm UTC](https://forum.crystal-lang.org/t/file-uploader-crystal-a-simple-file-uploader-made-on-crystal-using-kemal/7058/3 "2024-08-06T19:18:51Z")

</div>

Great project, thanks a lot for using Kemal ❤

---

<div class="post-metadata">

**Author:** ![Fijxu](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fijxu/32/3422_2.png) [@Fijxu](https://forum.crystal-lang.org/u/Fijxu)\
**Post date:** [June 4, 2025, 2:17am UTC](https://forum.crystal-lang.org/t/file-uploader-crystal-a-simple-file-uploader-made-on-crystal-using-kemal/7058/4 "2025-06-04T02:17:41Z")

</div>

So, anyone remembers this project? It has been heavily rewritten and now it’s called “Patchy”

> **[patchy](https://codeberg.org/Fijxu/patchy)**
>
> Temporary file uploader. Mirror of https://git.nadeko.net/Fijxu/patchy (issues and PRs can be also opened here!)

Is no longer a **simple** file uploader as I plan to add Abuse reports and a proper Admin panel and other features to make moderation of the content way easier instead of having to delete the files manually when something undesirable is uploaded.

I still need to make some improvements like limiting how many concurrent accesses can be made to a single file to prevent DoS attacks due to I/O exhaustion, so if a specific file ID is being retrieved at more than 100 req/s, we just limit the access to that specific file, from all IP addresses; Maybe not the best approach since this kind of rate limit can be done per IP address at the reverse proxy level like NGINX, but generally, people that do DDoS attacks to services use more than 1 IP address, so I think it’s better to restrict the access to the whole file temporarily.

The I/O exhaustation doesn’t happen with files that are **cached** since they are saved on ram of course.

And talking of cache, I also implemented a LRU cache which caches certain files depending of the size of them on memory.

> **[patchy/src/utils/cache.cr at main](https://codeberg.org/Fijxu/patchy/src/branch/main/src/utils/cache.cr)**
>
> patchy - Temporary file uploader. Mirror of https://git.nadeko.net/Fijxu/patchy (issues and PRs can be also opened here!)

It doesn’t make things any more faster if file cached is being retrieved just a few times, but I did it in case a file is accessed hundreds/thousand of times.

Here are some benchmarks with the cache using a ~498KiB file ;)

No cache, file being read directly from a SSD spamming [openat](https://linux.die.net/man/2/openat) syscalls

```auto
$ wrk -c 100 http://localhost:10006/-/file/vZ
Running 10s test @ http://localhost:10006/-/file/vZ
  2 threads and 100 connections
  Thread Stats Avg Stdev Max +/- Stdev
    Latency 35.20ms 16.82ms 100.25ms 73.27%
    Req/Sec 1.42k 173.58 2.17k 76.00%
  28296 requests in 10.02s, 13.46GB read
Requests/sec: 2823.61
Transfer/sec: 1.34GB

```

```auto
$ wrk -c 100 http://localhost:10006/-/file/vZ
Running 10s test @ http://localhost:10006/-/file/vZ
  2 threads and 100 connections
  Thread Stats Avg Stdev Max +/- Stdev
    Latency 18.87ms 3.08ms 39.52ms 83.04%
    Req/Sec 2.60k 252.05 3.02k 67.50%
  51913 requests in 10.04s, 24.61GB read
Requests/sec: 5168.75
Transfer/sec: 2.45GB

```

Around 58% more req/s while using Cache. This may be useful for people that have a lot of RAM on their servers and serve a lot of files so that is why I did that in the first place ;3

* * *

The old code that existed before I renamed it to “patchy” was awful, I didn’t use struct nor classes at all, just, Tuples and some NamedTuples, it was horrible, lol.
