{"id":19777974,"url":"https://github.com/shrinerb/shrine-tus","last_synced_at":"2025-04-30T20:30:36.980Z","repository":{"id":56895456,"uuid":"87725003","full_name":"shrinerb/shrine-tus","owner":"shrinerb","description":"Plugin and storage for integrating Shrine and tus-ruby-server","archived":false,"fork":false,"pushed_at":"2020-08-20T07:12:18.000Z","size":54,"stargazers_count":10,"open_issues_count":1,"forks_count":1,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-05-22T17:34:17.121Z","etag":null,"topics":["direct-upload","shrine","storage","tus","tus-ruby-server"],"latest_commit_sha":null,"homepage":"https://tus.io/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shrinerb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-09T17:07:07.000Z","updated_at":"2024-05-04T22:50:58.000Z","dependencies_parsed_at":"2022-08-21T01:20:55.580Z","dependency_job_id":null,"html_url":"https://github.com/shrinerb/shrine-tus","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shrinerb%2Fshrine-tus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shrinerb%2Fshrine-tus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shrinerb%2Fshrine-tus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shrinerb%2Fshrine-tus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shrinerb","download_url":"https://codeload.github.com/shrinerb/shrine-tus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224177531,"owners_count":17268693,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["direct-upload","shrine","storage","tus","tus-ruby-server"],"created_at":"2024-11-12T05:27:17.785Z","updated_at":"2024-11-12T05:27:18.365Z","avatar_url":"https://github.com/shrinerb.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Shrine::Tus\n\nProvides tools for integrating [Shrine] with [tus-ruby-server].\n\n## Installation\n\n```rb\ngem \"shrine-tus\", \"~\u003e 2.0\"\n```\n\n## Usage\n\nWhen the file is uploaded to the tus server, you'll probably want to attach\nit to a database record and move it to permanent storage. The storage that\ntus-ruby-server uses should be considered temporary, as some uploads might\nnever be finished, and tus-ruby-server needs to clear expired uploads.\n\nShrine-tus provides **three ways** of moving the file uploaded into the tus\nserver to permanent storage, which differ in performance and level of\ndecoupling. But regardless of the approach you choose, the code for attaching\nthe uploaded file works the same:\n\n```rb\nclass VideoUploader \u003c Shrine\n  # ...\nend\n```\n```rb\nclass Movie \u003c Sequel::Model\n  include VideoUploader::Attachment.new(:video)\nend\n```\n```rb\nfile_data #=\u003e {\"id\":\"http://tus-server.org/68db42638388ae645ab747b36a837a79\", \"storage\":\"cache\", \"metadata\":{...}}\nMovie.create(video: file_data)\n```\n\nSee [shrine-tus-demo] for an example application that uses shrine-tus.\n\nNote that by default **Shrine won't extract metadata from directly upload\nfiles**, instead it will just copy metadata that was extracted on the client\nside. See [this section][metadata direct uploads] for the rationale and\ninstructions on how to opt in.\n\n### Approach A: Downloading through tus server\n\nConceptionally the simplest setup is to have Shrine download the uploaded file\nthrough your tus server app. This is also the most decoupled option, because\nyour main app can remain completely oblivious to what storage the tus server\napp uses internally, or in which programming language is it written (e.g. you\ncould swap it for [tusd] and everything should continue to work the same).\n\nTo use this approach, you need to assign `Shrine::Storage::Tus` as your\ntemporary storage:\n\n```rb\nrequire \"shrine/storage/tus\"\n\nShrine.storages = {\n  cache: Shrine::Storage::YourTemporaryStorage.new(...),\n  store: Shrine::Storage::YourPermanentStorage.new(...),\n  tus:   Shrine::Storage::Tus.new\n}\n```\n```rb\nclass VideoUploader \u003c Shrine\n  storages[:cache] = storages[:tus] # set Shrine::Storage::Tus as temporary storage\nend\n```\n\n`Shrine::Storage::Tus` is a subclass of `Shrine::Storage::Url`, which uses\n[Down] for downloading. By default, the `Down::Http` backend is used, which is\nimplemented using [HTTP.rb].\n\nIf you're experiencing a lot of network hiccups while downloading, you might\nwant to consider switching to the `Down::Wget` backend, as `wget` automatically\nresumes the download in case of network hiccups.\n\n```rb\nShrine::Storage::Tus.new(downloader: :wget)\n```\n\n### Approach B: Downloading directly from storage\n\nWhile the appoach **A** is decoupled from the tus server implementation, it\nmight not be the most performant depending on the overhead between your main\napp and the tus server app, how well the web server that you use for the tus\nserver app handles streaming downloads (whether it blocks the web worker, thus\naffecting the app's request throughput), and whether you have hard request\ntimeout limits like on Heroku. Down will also temporarily cache downloaded\ncontent to disk while copying to the permanent storage, so that adds I/O and\ndisk usage.\n\nInstead of downloading through the tus server app, you can download directly\nfrom the underlying storage that it uses. This requires that you have the tus\nstorage configured in your main app (which might already by the case if you're\nrunning tus server in the same process as your main app), and you can pass that\nstorage to `Shrine::Storage::Tus`:\n\n```rb\nShrine::Storage::Tus.new(tus_storage: Tus::Server.opts[:storage])\n```\n\n### Approach C: Tus storage equals Shrine storage\n\nApproach **B** internally utilizes the common interface of each tus storage\nobject for streaming the file uploaded to that storage, via `#each`. However,\ncertain Shrine storage classes have optimizations when copying/moving a file\nbetween two storages of the **same kind**.\n\nSo if you want to use the same kind of permanent storage as your tus server\nuses, you can reap those performance benefits. In order to do this, instead of\nusing `Shrine::Storage::Tus` as your temporary Shrine storage as we did in\napproaches A and B, we will be using a regular Shrine storage which will match\nthe storage that the tus server uses. In other words, your Shrine storage and\nyour tus storage would reference the same files. So, if your tus server app is\nconfigured with either of the following storages:\n\n```rb\nTus::Server.opts[:storage] = Tus::Storage::Filesystem.new(\"data\")\nTus::Server.opts[:storage] = Tus::Storage::Gridfs.new(client: mongo, prefix: \"tus\")\nTus::Server.opts[:storage] = Tus::Storage::S3.new(prefix: \"tus\", **s3_options)\n```\n\nThen Shrine should be configured with the corresponding temporary storage:\n\n```rb\nShrine.storages[:cache] = Shrine::Storage::FileSystem.new(\"data\")\nShrine.storages[:cache] = Shrine::Storage::Gridfs.new(client: mongo, prefix: \"tus\")\nShrine.storages[:cache] = Shrine::Storage::S3.new(prefix: \"tus\", **s3_options)\n```\n\nIn approaches **A** and **B** we didn't need to change the file data received\nfrom the client, because we were using a subclass of `Shrine::Storage::Url`,\nwhich accepts the `id` field as a URL. But with this approach the `id` field\nwill need to be translated from the tus URL to the correct ID for your\ntemporary Shrine storage, using the `tus` plugin that ships with shrine-tus.\n\n```rb\nShrine.storages = {\n  cache: Shrine::Storage::YourTemporaryStorage.new(...),\n  store: Shrine::Storage::YourPermanentStorage.new(...),\n}\n```\n```rb\nclass VideoUploader \u003c Shrine\n  plugin :tus\nend\n```\n\nNote that it's **not** recommended to use the `delete_promoted` Shrine plugin\nwith this this approach, because depending on the tus storage implementation\nit could cause HEAD requests to the tus server app to return a success for files\nthat were deleted by Shrine.\n\nThese are the performance advantages for each of the official storages:\n\n#### Filesystem\n\n`Shrine::Storage::FileSystem` will have roughly the same performance as in\noption **B**, though it will allocate less memory. However, if you load the\n`moving` plugin, Shrine will execute a `mv` command between the tus storage\nand permanent storage, which executes instantaneously regardless of the\nfilesize.\n\n```rb\nShrine.plugin :moving\n```\n\n#### Mongo GridFS\n\n`Shrine::Storage::Gridfs` will use more efficient copying, resulting in up to\n2x speedup according to my benchmarks.\n\n#### AWS S3\n\n`Shrine::Storage::S3` will issue a single S3 COPY request for files smaller\nthan 100MB, while files 100MB or larger will be divided into multiple chunks\nwhich will be copied individually and in parallel using S3's multipart API.\n\n## Contributing\n\n```sh\n$ bundle exec rake test\n```\n\n## License\n\n[MIT](/LICENSE.txt)\n\n[Shrine]: https://github.com/shrinerb/shrine\n[tus-ruby-server]: https://github.com/janko/tus-ruby-server\n[Down]: https://github.com/janko/down\n[HTTP.rb]: https://github.com/httprb/http\n[shrine-tus-demo]: https://github.com/shrinerb/shrine-tus-demo\n[tusd]: https://github.com/tus/tusd\n[metadata direct uploads]: https://github.com/shrinerb/shrine/blob/master/doc/metadata.md#direct-uploads\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshrinerb%2Fshrine-tus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshrinerb%2Fshrine-tus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshrinerb%2Fshrine-tus/lists"}