Image upload using SolidJS and Cloudinary

Harsh Mangalam - Apr 5 '22 - - Dev Community

In this post we will upload Image from local to cloudinary. We will create useCloudinary hook that will take care of the stuff related to file upload.

Libraries

  • SolidJS
  • Axios
  • SUID
  • Shortid

Features

  • Image preview
  • Upload to Cloudinary
  • Progress bar
  • Abort image upload

src/hooks/useCloudinary.jsx

import axios from "axios";
import shortid from "shortid";
import { createStore } from "solid-js/store";

const url = `https://api.cloudinary.com/v1_1/${
  import.meta.env.VITE_CLOUDINARY_CLOUD_NAME
}/image/upload`;

export default function useCloudinary() {
  const [store, setStore] = createStore({
    image: null,
    imagePreview: "",
    uploadProgress: 0,
    alert: null,
    abortToken: null,
  });

  function handleImageChange(e) {
    const image = e.target.files[0];
    // create blob url of selected image for preview
    const imagePreview = URL.createObjectURL(image);
    // create axios cancellation token to abort request in future
    const abortToken = axios.CancelToken.source();

    setStore("image", image);
    setStore("imagePreview", imagePreview);
    setStore("abortToken", abortToken);
    setStore("alert", {
      severity: "success",
      text: "Image loaded successfully",
    });
  }
  function handleImageRemove() {
    // cleanup blob  metadata
    URL.revokeObjectURL(store.imagePreview);
    window.location.reload();
  }
  async function handleImageUpload() {
    try {
      const formData = new FormData();
      formData.append("file", store.image);
      formData.append(
        "upload_preset",
        import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET
      );
      formData.append("api_key", import.meta.env.VITE_CLOUDINARY_API_KEY);
      formData.append("public_id", shortid.generate());

      const response = await axios.post(url, formData, {
        onUploadProgress: handleUploadProgess,
        cancelToken: store.abortToken.token,
      });
      setStore("alert", {
        severity: "success",
        text: "Image uploaded to cloudinary successfully",
      });

      // revoke preview blob url
      URL.revokeObjectURL(store.imagePreview);
      setStore("imagePreview", response.data.url);
    } catch (error) {
      console.log(error);
    }
  }

  function handleUploadProgess(progressEv) {
    const progress = Math.floor((progressEv.loaded / store.image.size) * 100);
    console.log(progress);
    setStore("uploadProgress", progress);
  }

  function handleCancelUpload() {
    store.abortToken.cancel();
    setStore("alert", {
      severity: "error",
      text: "Image upload aborted",
    });
  }
  return {
    store,
    handleImageChange,
    handleImageRemove,
    handleImageUpload,
    handleCancelUpload,
  };
}


Enter fullscreen mode Exit fullscreen mode

we have created a new store and initialise with initial value.

image field will store image selected from local.

imagePreview field will store image url for image preview
and cloudinary url after successfully uploaded.

uploadProgress field will show percentage of image data
uploaded to cloudinary.

alert show success, error and warning message.

abortToken field will store Axios CancelTokenSource which
help to abort request in middle.

handleImageChange() function create blob url of image for
preview and axios cancellation token which we can use in
future to cancel request in middle.

handleImageRemove() method remove preview image and revoke
blob url to clean memory acquired by blob metadata.

handleImageUpload() function upload image to cloudinary
using axios post request and when image successfully
uploaded it revoke blob url and show image from cloudinary
url.

handleUploadProgess() function track uploaded chunk of
image data.

handleCancelUpload() function cancel axios request.

Create .env file in project root and add required environment variable.

.env

VITE_CLOUDINARY_UPLOAD_PRESET=
VITE_CLOUDINARY_API_KEY=
VITE_CLOUDINARY_CLOUD_NAME=

Enter fullscreen mode Exit fullscreen mode

Prefix with VITE_ is required if you want to access your environment variable in browser.

We will create UI using SUID library. SUID is a Component Material design for Solidjs ported from MUI React.


import { ThemeProvider } from "@suid/material";
import Alert from "@suid/material/Alert";
import Button from "@suid/material/Button";
import Card from "@suid/material/Card";
import CardActions from "@suid/material/CardActions";
import CardMedia from "@suid/material/CardMedia";
import Container from "@suid/material/Container";
import Grid from "@suid/material/Grid";
import LinearProgress from "@suid/material/LinearProgress";
import Stack from "@suid/material/Stack";
import { Show } from "solid-js";
import useCloudinary from "./hooks/useCloudinary";

function App() {
  let fileInputRef = null;
  const {
    store,
    handleImageChange,
    handleImageRemove,
    handleImageUpload,
    handleCancelUpload,
  } = useCloudinary();
  return (
    <ThemeProvider>
      <Container>
        <Grid container sx={{ justifyContent: "center" }}>
          <Grid item md={6} xs={12}>
            <Show when={store.alert}>
              <Alert sx={{ mt: 4, mb: 4 }} severity={store.alert.severity}>
                {store.alert.text}
              </Alert>
            </Show>

            <input
              type="file"
              hidden
              ref={fileInputRef}
              accept="image/*"
              onChange={handleImageChange}
            />
            <Button
              onClick={() => fileInputRef.click()}
              variant="contained"
              size="large"
            >
              Select Image
            </Button>

            <Show when={store.uploadProgress}>
              <Stack direction={"column"} spacing={2}>
                <LinearProgress
                  sx={{ mt: 4 }}
                  variant="determinate"
                  value={store.uploadProgress}
                />
                <Button
                  variant="contained"
                  color="error"
                  onClick={handleCancelUpload}
                >
                  Cancel Upload
                </Button>
              </Stack>
            </Show>

            <Show when={store.imagePreview}>
              <Card sx={{ mt: 4 }}>
                <CardMedia
                  component="img"
                  height="600px"
                  image={store.imagePreview}
                  alt="Image Preview"
                />

                <CardActions>
                  <Button
                    variant="contained"
                    color="error"
                    onClick={handleImageRemove}
                  >
                    Remove
                  </Button>
                  <Button
                    variant="contained"
                    color="success"
                    onClick={handleImageUpload}
                  >
                    Upload
                  </Button>
                </CardActions>
              </Card>
            </Show>
          </Grid>
        </Grid>
      </Container>
    </ThemeProvider>
  );
}

export default App;



Enter fullscreen mode Exit fullscreen mode

GitHub logo harshmangalam / solid-cloudinary-image-upload

Image upload using solidjs and cloudinary

Usage

Those templates dependencies are maintained via pnpm via pnpm up -Lri.

This is the reason you see a pnpm-lock.yaml. That being said, any package manager will work. This file can be safely be removed once you clone a template.

$ npm install # or pnpm install or yarn install
Enter fullscreen mode Exit fullscreen mode

Learn more on the Solid Website and come chat with us on our Discord

Available Scripts

In the project directory, you can run:

npm dev or npm start

Runs the app in the development mode.
Open http://localhost:3000 to view it in the browser.

The page will reload if you make edits.

npm run build

Builds the app for production to the dist folder.
It correctly bundles Solid in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes.
Your app is ready to be deployed!

Deployment

You can…

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .