Saint Everest
Saint Everest

Reputation: 41

How to upload Image from Next JS Strapi API

How can I add an image from NextJS to Strapi Media library? I Try to upload the image from the NextJS frontend, the image will be uploaded to my Strapi Media library and my Cloudinary account but the image will not be associated/linked to that particular post Here is my code

path: components/ImageUpload.js

import { useState } from "react";
import { API_URL } from "../config/index";
import styles from "@/styles/FormImage.module.css";

export default function ImageUpload({ sportNewsId, imageUploaded }) {
  const [image, setImage] = useState(null);

  const handleFilechange = (e) => {
    console.log(e.target.files);
    setImage(e.target.files[0]);
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    const formData = new FormData();
    formData.append("files", image);
    formData.append("ref", "sports");
    formData.append("refid", sportNewsId);
    formData.append("field", "image");

    const res = await fetch(`${API_URL}/upload`, {
      method: "POST",
      body: formData,
    });
    if (res.ok) {
      imageUploaded();
    }
  };

  return (
    <div className={styles.form}>
      <h4>Upload Sport News Image</h4>
      <form onSubmit={handleSubmit}>
        <div className={styles.file}>
          <input type="file" onChange={handleFilechange} />
          <input type="submit" value="Upload" className="btn" />
        </div>
      </form>
    </div>
  );
}

path:pages/news/edit/[id].js

import Link from "next/link";
import { useState } from "react";
import Image from "next/image";
import { useRouter } from "next/router";
import moment from "moment";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import Layout from "@/components/Layout";
import { API_URL } from "@/config/index";
import styles from "@/styles/FormEdit.module.css";
import Modal from "@/components/Modal";
import ImageUpload from "@/components/ImageUpload";

export default function EditNews({ sportNews }) {
  const [values, setValues] = useState({
    name: sportNews.name,
    detail: sportNews.detail,
    date: sportNews.date,
    time: sportNews.time,
  });
  const [previewImage, setPreviewImage] = useState(
    sportNews.image ? sportNews.image.formats.thumbnail.url : null
  );
  const [showModal, setShowModal] = useState(false);
  const router = useRouter();
  const { name, detail, date, time } = values;
  const handleSubmit = async (e) => {
    e.preventDefault();
    const emptyFieldCheck = Object.values(values).some(
      (element) => element === ""
    );
    if (emptyFieldCheck) {
      toast.error("Please fill all input field");
    }
    const response = await fetch(`${API_URL}/sports/${sportNews.id}`, {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(values),
    });
    if (!response.ok) {
      toast.error("something went wrong!!!");
    } else {
      const sport = await response.json();
      router.push(`/news/${sport.slug}`);
    }
  };

  const imageUploaded = async (e) => {
    const res = await fetch(`${API_URL}/sports/${sportNews.id}`);
    const data = await res.json();
    console.log("showing =>", data);
    console.log(setPreviewImage);
    setPreviewImage(data.image[0].formats.thumbnail.url);
    setShowModal(false);
  };

  const handleInputchange = (e) => {
    const { name, value } = e.target;
    setValues({ ...values, [name]: value });
  };
  return (
    <Layout title="Add New Sport News">
      <Link href="/news">Go Back</Link>
      <h2>Add Sport News</h2>
      <ToastContainer />
      <form onSubmit={handleSubmit} className={styles.form}>
        <div className={styles.grid}>
          <div>
            <label htmlFor="name">Name</label>
            <input
              name="name"
              id="name"
              type="text"
              value={name}
              onChange={handleInputchange}
            />
          </div>
          <div>
            <label htmlFor="date">Date</label>
            <input
              name="date"
              id="date"
              type="date"
              value={moment(date).format("yyyy-MM-DD")}
              onChange={handleInputchange}
            />
          </div>
          <div>
            <label htmlFor="time">Time</label>
            <input
              name="time"
              id="time"
              type="text"
              value={time}
              onChange={handleInputchange}
            />
          </div>
        </div>
        <div>
          <label htmlFor="detail">Detail</label>
          <textarea
            name="detail"
            id="detail"
            type="text"
            value={detail}
            onChange={handleInputchange}
          />
        </div>
        <input className="btn" type="submit" value="Add News" />
      </form>
      {/* {console.log(previewImage)} */}
      {previewImage ? (
        <Image src={previewImage} height={100} width={180} />
      ) : (
        <div>
          <p>No Image Available</p>
        </div>
      )}
      <div>
        <button onClick={() => setShowModal(true)} className="btn-edit">
          Update Image
        </button>
      </div>
      <Modal show={showModal} onClose={() => setShowModal(false)}>
        <ImageUpload sportNewsId={sportNews.id} imageUploaded={imageUploaded} />
      </Modal>
    </Layout>
  );
}
export async function getServerSideProps({ params: { id } }) {
  const res = await fetch(`${API_URL}/sports/${id}`);
  const sportNews = await res.json();
  return {
    props: { sportNews },
  };
}

this is the error message it is showing. error message

how do I resolve this error, any assistance will be appreciated Thanks a lot

Upvotes: 0

Views: 2354

Answers (2)

Camille Vingere
Camille Vingere

Reputation: 136

For a formData you have to add a header :

'Content-Type': 'multipart/form-data'

I have been struggling during hours to find this. I am uploading a file directly from an entry and not with the /upload route but it might work the same way. Using axios for the post method here is an example :

  const form = new FormData();

  const postData = {
    name: 'test2',
  };
  form.append('files.image', file);
  form.append('data', JSON.stringify(postData));
  await axios
    .post(getStrapiURL('/ingredients'), form, {
      headers: {
        'Content-Type': 'multipart/form-data',
      },
    })
    .then((response) => {
      // Handle success.
      console.log('Well done!');
      console.log('Data: ', response.data);
    })
    .catch((error) => {
      // Handle error.
      console.log('An error occurred:', error.response);
    });

Upvotes: 2

eugene musebe
eugene musebe

Reputation: 56

From my observation, the problem is on the setPreviewImage line remove the [0] array brackets from the image in order to access the Cloudinary thumbnail Url you will get from the Strapi API after each image upload.

The function below should make it work

const imageUploaded = async (e) => {
    const res = await fetch(`${API_URL}/sports/${sportNews.id}`);
    const data = await res.json();
    console.log("showing =>", data);
    console.log(setPreviewImage);
    setPreviewImage(data.image.formats.thumbnail.url);
    setShowModal(false);
  };

Upvotes: 1

Related Questions