Using AWS Amplify Storage without credentials

Viewed 31

I am trying to allow images stored in a public path on an S3 bucket to be retrieved by any guest before they are logged in with credentials.

After calling Storage.get(note.image) in fetchNotes() I am met with this console error:

TypeError: Cannot read properties of undefined (reading 'byteLength')

However, Storage.put() and Storage.get() work successfully for users who are logged in with credentials.

I know the public path is setup correctly for public read access when I navigate to the object URL's in the browser, but I think Amplify may not be confiurged properly for public read access on the bucket.

Here is the relevant code:

import React, { useState, useEffect } from 'react';
import './App.css';
import { Authenticator, useAuthenticator  } from '@aws-amplify/ui-react';
import { listNotes } from './graphql/queries';
import { createNote as createNoteMutation, deleteNote as deleteNoteMutation } from './graphql/mutations';
import { API, Storage } from 'aws-amplify';

const initialFormState = { name: '', description: '' }

function App() {
  const [notes, setNotes] = useState([]);
  const [formData, setFormData] = useState(initialFormState);
  const { user, authStatus } = useAuthenticator((context) => [context.user, context.authStatus ]);

  useEffect(() => {
    fetchNotes();
  }, []);

  async function deleteNote({ id }) {
    const newNotesArray = notes.filter(note => note.id !== id);
    setNotes(newNotesArray);
    await API.graphql({
      query: deleteNoteMutation,
      variables: { input: { id } },
      authMode: "AMAZON_COGNITO_USER_POOLS"
    });
  }

  async function onChange(e) {
    console.log(e.target.files[0]);
    if (!e.target.files[0]) return
    const file = e.target.files[0];
    setFormData({ ...formData, image: file.name });
    await Storage.put(file.name, file);
    fetchNotes();
  }

  async function fetchNotes() {
    console.log("ftech");
    const apiData = await API.graphql({ 
      query: listNotes
     });
    const notesFromAPI = apiData.data.listNotes.items;
    await Promise.all(notesFromAPI.map(async note => {
      if (note.image) {
        const image = await Storage.get(note.image, {config: {level: 'public' }}).catch((err) => { console.error(err); });
        if (image) {
          note.image = image;
        }
      }
      return note;
    }))
    setNotes(apiData.data.listNotes.items);
    console.log(notes);
  }

  async function createNote() {
    if (!formData.name || !formData.description) return;
    await API.graphql({
      query: createNoteMutation,
      variables: { input: formData },
      authMode: "AMAZON_COGNITO_USER_POOLS"
    });
    if (formData.image) {
      const image = await Storage.get(formData.image);
      formData.image = image;
    }
    setNotes([...notes, formData]);
    setFormData(initialFormState);
  }

  return (
    <div className="App">
      <Authenticator>
        {({ signOut, user }) => (
          <div>

            <h1>My Notes App</h1>
            <form onSubmit={createNote}>
              <input
                type="text"
                onChange={e => setFormData({ ...formData, 'name': e.target.value })}
                placeholder="Note name"
                value={formData.name}
              />
              <input
                type="text"
                onChange={e => setFormData({ ...formData, 'description': e.target.value })}
                placeholder="Note description"
                value={formData.description}
              />
              <input
                type="file"
                onChange={onChange}
              />
              <button type="submit">Create Note</button>
            </form>
            <div>
                <h1>Hello {user.username}</h1>
              </div>
            <button onClick={signOut}>Sign out</button>
          </div>
        )}
      </Authenticator>
      <div style={{ marginBottom: 30 }}>
        {
          notes.map(note => (
            <div key={note.id || note.name}>
              <h2>{note.name}</h2>
              <p>{note.description}</p>
              <p>{note.owner}</p>
              <p>{note.createdAt}</p>
              {
                (authStatus === 'authenticated') && (user.username === note.owner) && <button onClick={() => deleteNote(note)}>Delete note</button>
              }
              {
                note.image && <img src={note.image} style={{ width: 400 }} />
              }
            </div>
          ))
        }
      </div>
    </div>

  );
}

export default App;

Thanks for your input!

0 Answers
Related