GoulvenClech

GoulvenClech

Dropzone occasionally returns broken image with GCS error "The specified key does not exist"

Hi everyone,

Over the past few days, many customers have been experiencing issues with our image dropzone. The page ends up with broken images leading to an error “The specified key does not exist”, such as here:
https://storage.googleapis.com/encheres-immo-public-assets/thumbnail-6841abbf-034c-4da0-be18-53ec38450215

This bug is giving me trouble because nobody in the team has been able to reproduce this bug, and our Dropzone (using Dropzone.js and GCS) was coded over three years ago by a former employee.

Currently, I have two leads.

  1. Since the bug doesn’t affect the majority of my customers, maybe it’s related to the JavaScript hook running in their browser? Maybe the XMLHttpRequest can be blocked client side ?

Here is the hook :

assets/js/dropzone-live.js
import Dropzone from "dropzone"

export default {
  mounted() {
    let _this = this
    this.files = {}
    this.dropzone = new Dropzone(this.el, {
      url: ([file]) => file.signedUrl,
      method: "put",
      paramName: "asset",
      withCredentials: false,
      headers: {
        "Cache-Control": null,
        "X-Requested-With": null,
      },
      parallelUploads: 20,
      clickable: ".fileinput-button",
      thumbnailWidth: 600,
      thumbnailHeight: 450,
      thumbnailMethod: "contain",
      accept(file, done) {
        file.acceptCallback = done
        _this.files[file.upload.uuid] = file
        _this.pushEvent("added_file", {
          uuid: file.upload.uuid,
          name: file.name,
          type: file.type,
        })
      },
      thumbnail(file, url) {
        _this.el.querySelector(`img#asset-${file.upload.uuid}`).src = url
      },
      // Have to make this configuration to send raw files to GCS
      sending(file, xhr) {
        xhr.setRequestHeader("Content-Type", file.type)
        let _send = xhr.send
        xhr.send = function () {
          _send.call(xhr, file)
        }
      },
      success(file, response) {
        _this.pushEvent("upload_success", {
          upload_id: file.upload.uuid,
        })
      },
      uploadprogress(file, progress, bytesSent) {
        _this.pushEvent("upload_progress", {
          upload_id: file.upload.uuid,
          progress: progress,
        })
      },
    })
  },
  updated() {
    let _this = this
    this.el.querySelectorAll("img.new-asset").forEach((element) => {
      const uploadId = element.getAttribute("data-upload-uuid")
      let file = uploadId ? _this.files[uploadId] : null
      if (file && file.dataURL) {
        element.src = file.dataURL
      }
    })

    this.el.querySelectorAll(".file").forEach((element) => {
      const uploadId = element.getAttribute("data-upload-uuid")
      const signedUrl = element.getAttribute("data-upload-url")
      let file = uploadId ? _this.files[uploadId] : null

      if (file && file.status === "added") {
        file.signedUrl = signedUrl
        file.acceptCallback()
        _this.files[null]
      }
    })
  },
}

  1. I got a warning in Dropzone.ex , at :

[details=“handle_event(“added_file”)”]

  def handle_event(
        "added_file",
        %{"uuid" => upload_id, "name" => name, "type" => type},
        %{assigns: %{assets: assets}} = socket
      ) do
    upload_url =
      Application.get_env(:goth, :json)
      |> Poison.decode!()
      |> GcsSigner.Client.from_keyfile()
      |> GcsSigner.sign_url(@gcs_bucket, upload_id, verb: "PUT", content_type: type)

    url =
      upload_url
      |> URI.parse()
      |> Map.put(:query, nil)
      |> URI.to_string()

    asset = %{
      id: UUID.uuid4(),
      url: url,
      name: name,
      content_type: type,
      provider: "gcs",
      upload_id: upload_id,
      upload_url: upload_url,
      uploaded: false,
      new: true,
      progress: 0
    }

    {:noreply, assign(socket, :assets, assets ++ [asset])}
  end

[/details]

|> GcsSigner.Client.from_keyfile() raise the warning :

The function call will not succeed.

GcsSigner.sign_url(
  %GcsSigner.Client{:client_email => _, :private_key => _},
  <<101, 110, 99, 104, 101, 114, 101, 115, 45, 105, 109, 109, 111, 45, 112, 117, 98, 108,
    105, 99, 45, 97, 115, 115, 101, 116, 115>>,
  _upload_id :: any(),
  [{:content_type, _} | {:verb, <<_::24>>}, ...]
)

breaks the contract
(
  %{:client_email => String.t(), :private_key => String.t()},
  String.t(),
  String.t(),
  sign_url_opts()
) :: String.t()

(3.) Finally, I wonder if I shouldn’t get rid of all this legacy and undocumented code and redo this part in a more modern way. What are the Elixir libraries commonly used for file upload? I heard of Waffle for example?

I take all the opinions, suggestions, leads, help, and advice that could save me time. Thank you all!

Where Next?

Popular in Questions Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New

We're in Beta

About us Mission Statement