How to Display an Image Uploaded With Activestorage Rails

The writer selected the Diversity in Tech fund to receive a donation as part of the Write for DOnations plan.

Introduction

When you're edifice web applications that let users upload and shop files, you'll want to use a scalable file storage solution. This way you're not in danger of running out of space if your awarding gets wildly popular. Subsequently all, these uploads can be annihilation from profile pictures to house photos to PDF reports. You lot also want your file storage solution to be reliable then you lot don't lose your important customer files, and fast so your visitors aren't waiting for files to transfer. ou'll want this all to be affordable too.

DigitalOcean Spaces can address all of these needs. Considering it's compatible with Amazon'southward S3 service, you can quickly integrate it into a Ruby on Rails awarding using the new ActiveStorage library that ships with Rails 6.

In this guide, you'll configure a Rails application, so it uses ActiveStorage with DigitalOcean Spaces. You'll then run through the configuration necessary to become uploads and downloads blazing fast using direct uploads and Spaces' built-in CDN (Content Delivery Network).

When you're finished, you'll be ready to integrate file storage with DigitalOcean spaces into your ain Rails awarding.

Prerequisites

Before you begin this guide, you'll demand the following:

  • A DigitalOcean account.
  • A evolution surroundings for Ruby on Track. Follow How to Install Ruby on Rails with Rbenv on macOS or How to Install Ruby on Rails with Rbenv on Ubuntu depending on which operating organization you're using.
  • Some initial Ruby on Rails noesis. Tutorials like How to build a Ruby on Rails Awarding can assist with this, or you tin follow the Official Getting Started Guide.
  • Git installed on your development machine. You tin can follow the tutorial Contributing to Open Source: Getting Started with Git to install and set upward Git on your computer.
  • Node.js installed, which you can practise past post-obit How to Install Node.js and Create a Local Development Environment. This tutorial uses Node.js version 14.
  • The Yarn package managing director installed, which yous can install with npm install -g yarn.
  • SQLite installed, which you can practice by following Footstep 1 of How to Build a Ruby on Rail Application.
  • Imagemagick installed, which you tin install by post-obit the tutorial How To Resize Images with ImageMagick.
  • (Optional) If you'd like to experiment with the Spaces CDN, y'all'll need a domain proper noun and the ability to change DNS records for that domain. You tin follow the How to Add Domains tutorial to manage your domain with DigitalOcean.
  • (Optional) If y'all'd like to use Direct Uploads, you lot'll need to configure s3cmd to piece of work with DigitalOcean Spaces. Follow Setting Up s3cmd 2.10 with DigitalOcean Spaces to get that set up upward.

Pace 1 — Getting the Sample App Running

Rather than build a complete Rail application from scratch, you'll clone an existing Rails 6 application that uses ActiveStorage and modify information technology to use DigitalOcean Spaces equally its image storage backend. The app yous'll work with is Infinite Puppies, an image gallery that will let people upload and view photographs of their favorite puppies. The application looks like the post-obit figure:

The Space Puppies application running in a web browser

Open your terminal and clone the awarding from GitHub with the following control:

                      
  1. git clone https://github.com/do-community/space-puppies

You'll see output that looks similar to this:

                      

Output

Cloning into 'infinite-puppies'... remote: Enumerating objects: 122, done. remote: Counting objects: 100% (122/122), done. remote: Compressing objects: 100% (103/103), washed. remote: Total 122 (delta 3), reused 122 (delta three), pack-reused 0 Receiving objects: 100% (122/122), 163.17 KiB | 1018.00 KiB/s, washed. Resolving deltas: 100% (3/iii), done.

Side by side, check your Ruby version. Infinite Puppies uses Ruby 2.seven.1, so run rbenv versions to check which version you accept installed:

                      
  1. rbenv versions

If y'all've followed the prerequisite tutorials, yous'll only have Blood-red 2.v.1 in that listing, and your output will look like this:

                      

Output

* arrangement 2.5.1

If y'all don't have Ruby ii.7.1 in that list, install it using cherry-build:

                      
  1. rbenv install 2.seven.1

Depending on your auto's speed and operating arrangement, this might have a while. You'll see output that looks like this:

                      

Output

Downloading ruby-two.7.1.tar.bz2... -> https://enshroud.cherry-lang.org/pub/cherry-red/two.seven/ruby-2.7.1.tar.bz2 Installing ruby-two.7.i... Installed ruddy-two.7.ane to /root/.rbenv/versions/ii.7.1

Alter to the infinite-puppies directory:

                      
  1. cd space-puppies

rbenv will automatically change your Ruby version when y'all enter the directory. Verify the version:

                      
  1. ruby --version

You'll meet output similar to the following:

                      

Output

carmine ii.vii.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]

Next, you will install the Ruby gems and JavaScript packages that the app needs to run. Then y'all'll the database migrations needed for the Space Puppies app to run.

Install all the necessary gems using the parcel command:

                      
  1. package install

Then, to tell rbenv almost whatsoever new binaries installed by Bundler, use the rehash command:

                      
  1. rbenv rehash

Next, tell yarn to install the necessary JavaScript dependencies:

                      
  1. yarn install

Now create the database schema with Rails' built-in migration tool:

                      
  1. runway db:migrate

With all the libraries installed and the database created, start the built-in web server with the following control:

                      
  1. rails south

Note: By default, rails due south only binds to the local loopback address, pregnant you can merely admission the server from the same computer that runs the command. If you're running on a Droplet and yous'd like to access your server from a browser running on your local machine, you'll need to tell the Rails server to reply to remote requests by binding to 0.0.0.0. You tin exercise that with this command:

                          
  1. runway southward -b 0.0.0.0

Your server starts, and yous'll receive output like this:

                      

Output

=> Booting Puma => Rails 6.0.3.2 application starting in development => Run `rails server --help` for more startup options Puma starting in single way... * Version iv.3.5 (carmine ii.7.i-p83), codename: Mysterious Traveller * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://127.0.0.1:3000 * Listening on tcp://[::1]:3000 Use Ctrl-C to terminate

Now you tin can access your awarding in a spider web browser. If you're running the application on your local car, navigate to http://localhost:3000. If you're running on a Droplet or other remote server, then navigate to http://your_server_ip:3000.

You'll see the app's interface, but this fourth dimension without any puppies. Try calculation a couple of images by clicking the New Puppy button.

The Space Puppies application running in a web browser

If you need puppy photos to apply for testing, Unsplash has an all-encompassing listing you lot can use for testing. Review the Unsplash license if y'all plan to use these images in your projects.

Before moving on, allow'southward walk through each layer of the application and look at how ActiveStorage works with each part then you lot tin make the necessary changes for DigitalOcean Spaces. For a more detailed wait at ActiveStorage, read the Agile Storage Overview page in the official Rails documentation.

Commencement, look at the model, which represents an object in your awarding that you're storing in the database. You'll find the Puppy model in app/models/puppy.rb. Open this file in your text editor and you'll come across this code:

app/models/puppy.rb

                      course            Puppy            <            ApplicationRecord    has_one_attached            :photograph            end                  

Y'all'll find the has_one_attached macro in the model, which indicates there's a photograph attached to each Puppy model instance. These photos will be stored every bit ActiveStorage::Hulk instances via an ActiveStorage::Fastened::One proxy.

Close this file.

The next layer upwards the stack is the controller. In a Runway application, the controller is responsible for controlling admission to database models and responding to requests from the user. The respective controller for the Puppy model is the PuppiesController which you lot volition find in app/controllers/puppies_controller.rb. Open this file in your editor and you'll encounter the post-obit lawmaking:

app/controllers/puppies_controller.rb

                      class            PuppiesController            <            ApplicationController            def                          index                        @puppies            =            Puppy.with_attached_photo            terminate            # ... snipped other actions ...            cease                  

Everything in the file is standard Rail lawmaking, autonomously from the with_attached_photo call. This call causes ActiveRecord to load all of the associated ActiveStorage::Blob associations when you fetch the list of Puppy models. This is a scope that ActiveStorage provides to help you avoid an expensive North+1 database query.

Finally, let's await at the views, which generate the HTML your awarding will send to the user's browser. There are a few views in this app, just y'all'll want to focus on the view responsible for showing the uploaded puppy paradigm. Yous'll find this file at app/views/puppies/_puppy.html.erb. Open it in your editor, and you'll run across lawmaking like this:

app/views/puppies/_puppy.html.erb

                                                    <div              grade                              =                "puppy"                            >                                                      <%=                                  image_tag puppy.photo.variant(                  resize_to_fill                  :                  [                  250                  ,                  250                  ]                  )                                %>                                                                    </div              >                              

ActiveStorage is designed to work with Rails, and then yous tin can utilize the congenital-in image_tag helper to generate a URL that points to an attached photo, wherever it happens to exist stored. In this case, the app is using the variant support for images. When the user first requests this variant, ActiveStorage will automatically use ImageMagick via the image_processing gem, to generate a modified epitome fitting our requirements. In this case, information technology will create a puppy photo filling a 250x250 pixel box. The variant volition be stored for you in the same place as your original photo, which means yous'll only demand to generate each variant once. Rails volition serve the generated version on subsequent requests.

Annotation: Generating image variants can be boring, and y'all potentially don't want your users waiting. If you know you lot're going to need a particular variant, yous can eagerly generate it using the .processed method:

            puppy.photo.variant(              resize_to_fill              :              [              250              ,              250              ]              )              .processed                      

It'south a skilful idea to exercise this kind of processing in a background job when y'all deploy to product. Explore Agile Job and create a task to telephone call candy to generate your images ahead of fourth dimension.

Now your awarding is running locally, and you know how all the code pieces fit together. Side by side, information technology's time to set up a new DigitalOcean Space and so yous tin can move your uploads to the cloud.

Step two — Setting upward your DigitalOcean Infinite

At the moment, your Space Puppies application stores images locally, which is fine for evolution or testing, simply y'all nearly certainly don't want to use this mode in production. In order to scale the awarding horizontally by calculation more awarding server instances, you'd need copies of each image on every server.

In this footstep, yous'll create a DigitalOcean Space to use for your app's images.

Sign in to your DigitalOcean management console, click Create in the top right, and cull Spaces.

Pick any data center and leave the CDN disabled for at present; you'll come back to this afterward. Ensure the file listing is set to Restrict File Listing.

Choose a proper name for your Space. Recollect that this volition have to be unique across all Spaces users, and then option a unique name, like yourname-space-puppies. Click Create a Space:

A screenshot of the DigitalOcean create space form with a name filled  in

Alarm: Be careful near admission to the files you store on behalf of your customers. There take been many examples of information leaks and hacks due to misconfigured file storage. By default, ActiveStorage files are simply accessible if you lot generate an authenticated URL, but information technology'south worth beingness vigilant if you're dealing with customer data.

You'll so see your brand new Infinite.

Click the Settings tab and take a annotation of your Space'south endpoint. You'll demand that when y'all configure your Rails awarding.

Next, you'll configure the Track awarding to shop ActiveStorage files in this Infinite. To do that securely, you lot need to create a new Spaces Admission Fundamental and Secret.

Click API in the left navigation, and so click Generate New Central in the bottom right. Give your new cardinal a descriptive name similar "Evolution Machine". Your clandestine volition just appear in one case, and so be certain to re-create it somewhere condom for a moment.

A screenshot showing a Spaces access key

In your Rail app, you'll need a secure fashion to shop that access token, then you'll utilise Rails' secure credential management feature. To edit your credentials, execute the following command in your terminal:

                      
  1. EDITOR = "nano -w" rail credentials:edit

This generates a master key and launches the nano editor so y'all can edit the values.

In nano, add the following to your credentials.yml file, using your API key and secret from DigitalOcean:

config/credentials.yml

                      digitalocean            :            access_key            :            YOUR_API_ACCESS_KEY            secret            :            YOUR_API_ACCESS_SCRET                  

Relieve and close the file (Ctrl+X, and so Y, then Enter), and Rails will store an encrypted version that's safe to commit to source control in config/credentials.yml.enc.

Yous volition meet output like the post-obit:

                      

Output

Adding config/master.key to store the encryption key: RANDOM_HASH_HERE Save this in a password manager your team tin admission. If you lot lose the key, no one, including you, can access anything encrypted with it. create config/master.key File encrypted and saved.

Now that you've configured your credentials, you lot're ready to point your app to your new Spaces saucepan.

Open the file config/storage.yml in your editor and add the following definition to the bottom of that file:

config/storage.yml

                      digitalocean            :            service            :            S3            endpoint            :            https://your-spaces-endpoint-hither            access_key_id            :            <%= Rail.application.credentials.dig(:digitalocean,            :access_key) %>            secret_access_key            :            <%= Runway.application.credentials.dig(:digitalocean,            :secret) %>            bucket            :            your-space-proper name-here            region            :            unused                  

Note that the service says S3 rather than Spaces. Spaces has an S3-compatible API, and Rail supports S3 natively. Your endpoint is https:// followed by your Space'southward endpoint, which you copied previously, and the bucket name is the name of your Space, which you lot entered when creating it. The saucepan name is as well displayed as the championship in your Command Panel when you view your Space.

This configuration file will exist stored unencrypted, and so instead of entering your access fundamental and secret, y'all're referencing the ones you only entered securely in credentials.yml.enc.

Notation: DigitalOcean uses the endpoint to specify the region. Even so, you need to provide the region, or ActiveStorage will complain. Since DigitalOcean volition ignore it, y'all tin can set it to whatever value you'd similar. The value unused in the instance lawmaking makes it clear that you're not using it.

Salvage the configuration file.

Now, you need to tell Rail to use Spaces for your file storage backend instead of the local file system. Open config/environments/development.rb in your editor and change the config.active_storage.service entry from :local: to :digitalocean:

config/environments/development.rb

                      # ...            # Store uploaded files on the local file system (see config/storage.yml for options).            config.active_storage.service            =                          :digitalocean                        # ...                              

Save the file and exit your editor. Now offset your server again:

                      
  1. rail s -b 0.0.0.0

Visit http://localhost:3000 or http://your server ip:3000 in a browser in one case again.

Upload some images, and the app volition shop them in your DigitalOcean Space. You can see this by visiting your Space in the DigitalOcean console. Y'all will see the uploaded files and variants listed:

files uploaded to a Space

ActiveStorage uses random filenames by default, which is helpful when protecting uploaded client data. Metadata, including the original filename, is stored in your database instead.

Note: If you are getting an Aws::S3::Errors::SignatureDoesNotMatch, that might mean your credentials are incorrect. Run rails credentials:edit once more and double-check them.

Rails stores the names and some metadata about your files as ActiveStorage::Hulk records. Y'all can access the ActiveStorage::Blob for any of your records by calling an accessor method named subsequently your attachment. In this example, the attachment is called photo.

Try it out. Start a Runway console in your terminal:

                      
  1. rail c

Grab the blob from the last puppy photograph you uploaded:

                      >            Puppy.concluding.photo.hulk            #=> => #<ActiveStorage::Blob ...>                  

You now have a Rails Awarding storing uploads in a scalable, reliable, and affordable object store.

In the side by side two steps, you'll explore two optional additions you can brand to the app that volition assist amend this solution's functioning and speed for your users.

Pace iii — Configuring the Spaces CDN (Optional)

Note: For this step, you will need a doman with name servers pointing to DigitalOcean. You can follow the How to Add Domains guide to do that.

Using a Content Delivery Network (CDN) will allow you to provide faster downloads of files for your users by locating copies of the files closer to them.

You tin can investigate CDN operation using a tool similar Uptrends CDN Performance Bank check. If you add the URL for one of the photos you uploaded in the previous step, y'all'll see things are fast if you happen to be nearby, but things get a trivial slower as you lot move away geographically. You lot can become the URL using the Programmer Tools in your browser, or past starting a Rails console (rails c) and calling service_url on an attachment.

                      >            Puppy.last.photo.service_url                  

Here'south an instance Uptrends report with a file located in the San Francisco data center. Notice that the times decrease depending on the distance from San Francisco. San Diego has a short time, while Paris has a much longer time:

An example Uptrends CDN Performance Report

Yous tin can improve speeds by enabling Spaces' built-in CDN. Become to Spaces in your DigitalOcean Control Console and click the name of the Space you created in Step ii. Next, choose the Settings tab and click Edit next to CDN (Content Commitment Network), so click Enable CDN.

Now you lot need to cull a domain to utilise for your CDN and create an SSL Document for the domain. You can do this automatically using Let'southward Encrypt. Click the Use a custom subdomain dropdown and then Add a new subdomain certificate.

Detect the domain you'd similar to use, then cull the option to create a subdomain. Something like cdn.yourdomain.com is a standard naming convention. You tin can then requite the certificate a proper name and click the "Generate Document and Use Subdomain" push button.

The filled-in Add Custom Subdomain form

Printing the Relieve push button nether CDN (Content Commitment Network).

Your CDN is now enabled, but you lot need to tell your Rails Awarding to use it. This isn't congenital into ActiveStorage in this version of Rails, so y'all'll override some congenital-in Rails framework methods to go far work.

Create a new Rails initializer called config/initializers/active_storage_cdn.rb and add the post-obit code which will rewrite the URLs:

config/initializers/active_storage_cdn.rb

          Runway.application.config.after_initialize            exercise            require                          "active_storage/service/s3_service"                        module            SimpleCDNUrlReplacement            CDN_HOST            =                          "cdn.yourdomain.com"                        def                          url                        (            ...            )            url            =            super            original_host            =                          "                              #{                bucket.proper noun                }                            .                              #{                customer.customer.config.endpoint.host                }                            "                        url.gsub(original_host,            CDN_HOST            )            finish            end            ActiveStorage::Service::S3Service.            prepend            (SimpleCDNUrlReplacement)            end                  

This initializer runs each time your awarding asks for a URL from an ActiveStorage::Service::S3Service provider. Information technology and then replaces the original, not-CDN host with your CDN host, defined equally the CDN_HOST constant.

You can now restart your server, and yous'll notice that each of your photos comes from the CDN. Yous won't need to re-upload them, equally DigitalOcean will take care of forwarding the content from the information center where you lot set upward your Space out to the edge nodes.

You lot might similar to compare the speed of accessing one of your photos on Uptrends' Performance Bank check site now to the pre-CDN speed. Here'south an example of using the CDN on a San Francisco-based Space. You lot can come across a pregnant global speed comeback.

The Uptrends CDN Performance Report after enabling the CDN

Next you'll configure the awarding to receive files direct from the browser.

Stride 4 — Setting up Straight Uploads (Optional)

One terminal feature of ActiveStorage that you might like to consider is chosen a Direct Upload. At present, when your users upload a file, the data is sent to your server, candy by Rails, and so forwarded to your Space. This can crusade problems if you have many simultaneous users, or if your users are uploading large files, as each file will (in most cases) use a single app server thread for the entire duration of an upload.

Past dissimilarity, a Direct Upload will go straight to your DigitalOcean Infinite with no Rails server hop in between. To do this, you'll enable some born JavaScript that ships with Rails and configure Cantankerous-Origin Resource Sharing([CORS]((https://programmer.mozilla.org/en-Us/docs/Web/HTTP/CORS) on your Space so that you tin can securely send requests directly to the Space despite them originating in a different place.

First, you'll configure CORS for your Space. You will use s3cmd to exercise this, and you can follow Setting Upward s3cmd 2.ten with DigitalOcean Spaces if you haven't configured this to work with Spaces yet.

Create a new file called cors.xml and add together the following code to the file, replacing your_domain with the domain y'all're using for development. If you are developing on your local machine, yous'll utilize http://localhost:3000 . If you're developing on a Droplet, this will be your Droplet IP address:

cors.xml

                                                    <CORSConfiguration              >                                                      <CORSRule              >                                                      <AllowedOrigin              >                        your_domain                                          </AllowedOrigin              >                                                      <AllowedMethod              >            PUT                              </AllowedMethod              >                                                      <AllowedHeader              >            *                              </AllowedHeader              >                                                      <ExposeHeader              >            Origin                              </ExposeHeader              >                                                      <ExposeHeader              >            Content-Blazon                              </ExposeHeader              >                                                      <ExposeHeader              >            Content-MD5                              </ExposeHeader              >                                                      <ExposeHeader              >            Content-Disposition                              </ExposeHeader              >                                                      <MaxAgeSeconds              >            3600                              </MaxAgeSeconds              >                                                      </CORSRule              >                                                      </CORSConfiguration              >                              

You can then apply s3cmd to ready this every bit the CORS configuration for your Infinite:

                      
  1. s3cmd setcors cors.xml s3://your-space-name-hither

At that place's no output when this control runs successfully, just y'all tin can check that it worked past looking at your Space in the DigitalOcean Control Panel. Choose Spaces, then select the name of your Infinite, then select the Settings tab. You'll run across your configuration under the CORS Configurations heading:

A successful CORS configuration for direct uploads

Notation: At the moment y'all need to use s3cmd rather than the Control Panel to configure CORS for "localhost" domains because the Control Panel treats these every bit invalid domains. If you're using a non-localhost domain (like a Droplet IP) information technology's safe to practice it here.

Now you need to tell Rails to apply direct uploads, which y'all exercise by passing the direct_upload option to the file_field helper. Open app/views/puppies/new.html.erb in your editor and modify the file_field helper:

app/views/puppies/new.html.erb

                                                    <h2              >            New Puppy                              </h2              >                        <%= form_with(model: @puppy) practise |f| %>                                          <div              course                              =                "grade-item"                            >                        <%= f.label :photo %>     <%= f.file_field :photograph, take: "image/*",            direct_upload: truthful            %>                                          </div              >                                                      <div              class                              =                "form-detail"                            >                        <%= f.submit "Create puppy", class: "btn", data: { disable_with: "Creating..." } %>                                          </div              >                        <% end %>                  

Save the file and start your server again:

                      
  1. runway southward -b 0.0.0.0

When you upload a new photo, your photo is uploaded direct to DigitalOcean Spaces. You can verify this by looking at the PUT request that's made when you click the Create puppy button. You can find the requests by looking in your browser'south spider web console, or by reading the Rails server logs. You'll discover that the image upload is significantly faster, specially for larger images.

Conclusion

In this article yous modified a basic Rails application using ActiveStorage to store files that are secure, fast, and scalable on DigitalOcean Spaces. You configured a CDN for fast downloads no matter where your users are located, and you implemented direct uploads and then that your app servers will not be overwhelmed.

You lot tin now take this code and configuration and arrange it to fit your own Rail application.

kirchnersuchoson.blogspot.com

Source: https://www.digitalocean.com/community/tutorials/how-to-use-activestorage-in-rails-6-with-digitalocean-spaces

Belum ada Komentar untuk "How to Display an Image Uploaded With Activestorage Rails"

Posting Komentar

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel