Upload filer til Cloudinary med Vue & Vuetify

Skrevet af
Nicky Christensen
Udgivet
Opdateret

For noget tid siden arbejdede jeg på en SaaS platform hvor man blandt andet nemt skulle kunne uploade filer til en server. Valget faldte på Cloudinary, og kravet var at selve upload delen skulle ske via klienten (browseren). Dette betød der var 2 måder at håndtere det på, enten ved at benytte Cloudinarys egen upload widget eller ved at bygge det selv. Jeg valgte at bygge det selv, da upload widgetten fra Cloudinary er stor i filstørrelse, har ringe performance, og ikke har mange muligheder for styling.

Når projektet er blevet oprettet skal Vuetify tilføjes til projektet, også via terminalen “vue add vuetify” - Vælg “default” setup, og du er kørende.

Super, nu hvor der er et projekt, kan vi komme igang med lidt udvikling.

Start med at åbn dit favorit IDE, personligt selv benytter jeg WebStorm.

Rediger filen App.vue, da vi lige have fjernet lidt boilerplate kode herfra. Din fil må gerne ligne dette:

<template>
  <v-app>
    <v-app-bar
            app
            color="primary"
            dark>
    </v-app-bar>
    <v-content>
        <!-- Upload file here -->
    </v-content>
  </v-app>
</template>

<script>
  export default {
    name: 'App',
    data: () => ({
    //
    }),
  };
</script>

Nu hvor vores App.vue fil er helt ren for boilerplate kode kan vi starte. For at kunne uploade filer til Cloudinary skal vi bruge et input element af typen “file”. I dette tilfælde er Vuetify installeret i vores løsning, hvorfor vi kan bruge komponenten <v-file-input /> (Se dokumentation her på komponenten)

Denne komponent tilføjer vi til v-content

Komponenten skal være bundet op på et @change event. Dette er fordi vi gerne vil fange de filer som komponenten returnere når vi har valgt nogle filer fra filsystemet. Tilføj en funktion til methods. Jeg navngiver funktionen onAddFiles() - Herefter skal den bindes til komponenten via @change.

<v-content>
  <h1>
    Upload file to Cloudinary
  </h1>
  <v-file-input multiple label="Add your files" chips @change="onAddFiles" />
</v-content>

I onAddFiles() funktionen skal der tilføjes et parameter. Dette parameter vil i sidste ende indeholde de filer som bliver valgt fra filsystemet. Filerne bliver sendt komponenten <v-file-input /> og returnere et array af File Objects til selve funktionen.

Som du kan se i koden, skriver vi til console.log, hvilket gør vi i vores browser kan se hvad der bliver returneret til konsollen. Prøv det af og se hvad der sker i dit tilfælde.

<template>
  <v-app>
    <v-app-bar
    app
    color="primary"
    dark>
  </v-app-bar>
  <v-content>
<h1>
Upload file to Cloudinary
</h1>
<v-file-input multiple label="Add your files" chips @change="onAddFiles" />
</v-content>
</v-app>
</template>

<script>
  export default {
    name: 'App',
    data: () => ({
    }),
    methods: {
      onAddFiles(files) {
        window.console.log(files);
      }
    }
  };
</script>

Du skulle gerne få et array af filer vist i konsollen. Dette betyder nemlig at det virker indtil videre, og at vi har noget data at arbejde med. Næste skridt er at vi skal tage hver fil vi tilføjer og uploade filen til Cloudinary.

Hvis ikke du allerede har en konto hos Cloudinary, så opret den nu. Det er gratis. Når du har oprettet din konto er der et par ting vi skal bruge herfra. Gå til Settings -> upload, og klik på “Enable unsigned uploads”

cloudinary-1

Det skaber et upload preset som vi skal bruge for at kunne få lov at uploade filer. Dette skal senere hen tilføjes til koden sammen med vores “API base url”. URL hertil kan du finde under “Dashboard -> Account Details”

cloudinary-2

Nu hvor vi har de 2 værdier, kan vi gå tilbage til vores kode.

Cloudinary understøtter desværre ikke at man kan sende et array af filer til deres API, hvorfor det er nødvendigt at loope igennem alle filer, og tilføje hver enkelt.

onAddFiles(files) {
  if(files.length > 0) {
    files.forEach((file) => {
      window.console.log(file);
    });
 }
}

Som du kan se, looper vi over files og dermed kan fange hver file. Det næste der skal klares er at vi skal have lavet en funktion der kan tage hver fil og uploade den til Cloudinary.

I den funktion har vi behov for 2 vigtige ting, nemlig vores upload preset og vores API base url. Base URL skal udvides med /upload/ før at det vil virke.

Tilføj en ny funktion, jeg navngiver den: uploadFileToCloudinary() og tilføj følgende kode:

uploadFileToCloudinary(file) {
  return new Promise(function (resolve, reject) {
//Ideally these to lines would be in a .env file
const CLOUDINARY_URL = 'https://api.cloudinary.com/v1_1/CLOUDNAME/upload';
const CLOUDINARY_UPLOAD_PRESET = 'YOURUPLOADPRESET';

let formData = new FormData();
formData.append('upload_preset', CLOUDINARY_UPLOAD_PRESET);
formData.append('folder', 'cloudinary-demo');
formData.append('file', file);

let request = new XMLHttpRequest();
request.open('POST', CLOUDINARY_URL, true);
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

request.onreadystatechange = () => {
    // File uploaded successfully
    if (request.readyState === 4 && request.status === 200) {
        let response = JSON.parse(request.responseText);
        resolve(response);
    }

    // Not succesfull, let find our what happened
    if (request.status !== 200) {
        let response = JSON.parse(request.responseText);
        let error = response.error.message;
        alert('error, status code not 200 ' + error);
        reject(error);
    }

};

request.onerror = (err) => {
    alert('error: ' + err);
    reject(err);
};

request.send(formData);
});
}

Inden vi går videre skal vi lige have sat nogle værdier i data som vi får behov for senere.

Tilføj dette til data:

data: () => ({
  files: [],
  isError: false,
  errorText: null
})

Rediger funktionen onAddFiles(), og kald upload funktionen for hver fil. Når vi får svar fra vores metode, pusher vi svaret til vores files array som vi har sat i data.

onAddFiles(files) {
if (files.length > 0) {
  files.forEach((file) => {
    window.console.log(file);
    this.uploadFileToCloudinary(file).then((fileResponse) => {
        this.files.push(fileResponse);
    });
  });
}
}

Lad os også rette vores template til så vi kan se hver fil der bliver uploadet, og lad os ligeledes vise en fejl hvis det går galt.

<template>
<v-app>
<v-app-bar
        app
        color="primary"
        dark>
</v-app-bar>
<v-content style="padding:50px; margin-top:30px;">
    <!-- Upload file here -->
    <h1>
        Upload file to Cloudinary
    </h1>
    <v-file-input multiple label="Add your files" chips @change="onAddFiles" />

    <v-card v-if="files.length > 0">
        <v-card-text>
            <v-alert type="success" v-for="file in files" :key="file.public_id">
                File uploaded: {{file.original_filename}} at {{file.url}}
            </v-alert>
        </v-card-text>
    </v-card>

    <v-alert v-if="isError">
        {{errorText}}
    </v-alert>

</v-content>
</v-app>
</template>

Så er vi snart ved at være i mål vi mangler blot et par ting endnu. Lige nu laver vi en “alert” når der sker en fejl i funktionen uploadFileToCloudinary. Dette skal vi have tilrettet for at vi kan se det i vores applikation.

Erstat alle “alert” med følgende kode:

this.errorText = 'error uploading files ' + error;
this.isError = true;

Du er nu i mål. Du har nu en fuld funktionel applikation der kan upload filer til Cloudinary.

Dit slutresultat skulle gerne minde lidt om dette.

cloudinary-3

For at opsummere. Dette er en simpel, men effektiv og brugbar både at uploade filer til Cloudinary på. Som applikationen er pt er den meget simpel, men kan nemt udvides til at være meget mere avanceret og fleksibel.

Denne artikel skrev jeg oprindeligt på Medium, hvilket du kan finde her: Upload files to Cloudinary using Vue & Vuetify

Hele koden til ovenstående kan findes på GIT