'rails 7 ActionText: how to display images html from S3 in production?

Since a few days, I start to pull out my hair with a problem of displaying images stored on AWS S3. The images are sent to the S3 during creation, but it is not possible to display them in the HTML. I think I missed something in the configurations.

A little background: I have a Rails 7 application with a text editor (ActionText). Since ActionText has a direct-upload approach, I have configured the app to match that: here is how I did it in

 // direct_uploads.js

addEventListener("direct-upload:initialize", event => {
  const { target, detail } = event
  const { id, file } = detail
  target.insertAdjacentHTML("beforebegin", `
    <div id="direct-upload-${id}" class="direct-upload direct-upload--pending">
      <div id="direct-upload-progress-${id}" class="direct-upload__progress" style="width: 0%"></div>
      <span class="direct-upload__filename"></span>
    </div>
  `)
  target.previousElementSibling.querySelector(`.direct-upload__filename`).textContent = file.name
})

addEventListener("direct-upload:start", event => {
  const { id } = event.detail
  const element = document.getElementById(`direct-upload-${id}`)
  element.classList.remove("direct-upload--pending")
})

addEventListener("direct-upload:progress", event => {
  const { id, progress } = event.detail
  const progressElement = document.getElementById(`direct-upload-progress-${id}`)
  progressElement.style.width = `${progress}%`
})

addEventListener("direct-upload:error", event => {
  event.preventDefault()
  const { id, error } = event.detail
  const element = document.getElementById(`direct-upload-${id}`)
  element.classList.add("direct-upload--error")
  element.setAttribute("title", error)
})

addEventListener("direct-upload:end", event => {
  const { id } = event.detail
  const element = document.getElementById(`direct-upload-${id}`)
  element.classList.add("direct-upload--complete")
})

AWS configuration

**Bucket policy**
 {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "Statement1",
                "Effect": "Allow",
                "Principal": "*",
                "Action": [
                    "s3:DeleteObject",
                    "s3:GetObject",
                    "s3:ListBucket",
                    "s3:PutObject",
                    "s3:PutObjectAcl"
                ],
                "Resource": [
                    "arn:aws:s3:::bucket-prod.lnclass.com",
                    "arn:aws:s3:::bucket-prod.lnclass.com/*"
                ]
            }
        ]
    }

configuration of the bucket now let go for Cross-origin resource sharing (CORS) I think, since actionText uses direct-upload, it is necessary to configure CORS

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "HEAD",
            "GET",
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [
            "ETag"
        ]
    }
]

with this configuration the images are sent in the compartment, but not possible to load them on the site. and I do not know where I made the mistake

Edit: ActionText loads images stored on S3 from the .

view/active_storage/blobs/blob.html.erb

 <figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
  <% if blob.representable? %>
    <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %>
  <% end %>

  <figcaption class="attachment__caption">
    <% if caption = blob.try(:caption) %>
      <%= caption %>
    <% else %>
      <span class="attachment__name"><%= blob.filename %></span>
      <span class="attachment__size"><%= number_to_human_size blob.byte_size %></span>
    <% end %>
  </figcaption>
</figure>


Solution 1:[1]

Many thanks to [Rice][1] for his answer.

I detail a little, As actionText loads the images of S3 from the browser, it is necessary to be able to configure the url to load the images, then, here is how I made:

attachment--">
<% if blob.representable? %>
    <% if Rails.env == 'production' %>
       <img class: "actiontext-image" src="https://<%= Rails.application.credentials.dig(:aws, :s3_bucket) %>.s3-<%= Rails.application.credentials.dig(:aws, :s3_region) %>.amazonaws.com/<%= blob.key %>" />
            <% else %>
              <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]), class: "actiontext-image" %>
            <% end %>
          <% end %>
        
          <figcaption class="attachment__caption">
            <% if caption = blob.try(:caption) %>
              <%= caption %>
            <% else %>
              <span class="attachment__name"><%= blob.filename %></span>
              <span class="attachment__size"><%= number_to_human_size blob.byte_size %></span>
            <% end %>
          </figcaption>
        </figure>

for more security, I added the elements in an environment variable and used them in the view

region: <%= Rails.application.credentials.dig(:aws, :s3_region) %>
bucket: <%= Rails.application.credentials.dig(:aws, :s3_bucket) %>

note: so define the variables in the credentials. it's just the name of the region and the bucket

config/storage.yml
amazon:
  service: S3
  access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
  secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
  region: <%= Rails.application.credentials.dig(:aws, :s3_region) %>
  bucket: <%= Rails.application.credentials.dig(:aws, :s3_bucket) %>

Solution 2:[2]

It's due to this change in the detection of browser touchscreen capabilities in pull request #7029:

- // @property touch: Boolean
+ // @property touchNative: Boolean
  // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
- // This does not necessarily mean that the browser is running in a computer with
+ // **This does not necessarily mean** that the browser is running in a computer with
  // a touchscreen, it only means that the browser is capable of understanding
  // touch events.
- export var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
-       (window.DocumentTouch && document instanceof window.DocumentTouch));
export var touchNative = 'ontouchstart' in window || !!window.TouchEvent;

+ // @property touch: Boolean
+ // `true` for all browsers supporting either [touch](#browser-touch) or [pointer](#browser-pointer) events.
+ // Note: pointer events will be preferred (if available), and processed for all `touch*` listeners.
+ export var touch = !window.L_NO_TOUCH && (touchNative || pointer);

Whether a web brorwser supports touchscreens or not impacts the CSS classes applied to the Leaflet map container:

        DomUtil.addClass(container, 'leaflet-container' +
            (Browser.touch ? ' leaflet-touch' : '')

...which in turn impacts the CSS rules applied to the controls, e.g.

.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
    box-shadow: none;
    }

The logic in Leaflet 1.6.x does not detect touchscreen capabilities in your web browser, but the logic in 1.7.x does. The CSS rules just follow that.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 kamate vakaramoko
Solution 2 IvanSanchez