MeteorJS est une plateforme fullstack qui de part sa conception et l'utilisation du Distributed Data Protocol (DDP) change l'approche en matière de communication front/back. Comment faire pour gérer l'upload de fichiers dans ces conditions ?
Dernièrement je partageais avec vous ma façon d'organiser mon code lorsque j'utilise MeteorJS, vous pouvez retrouver cet article ici .
Mais comme je vous disais dans cet article, l'utilisation protocole DDP risque de casser vos habitudes en matière de communication serveur. Il ne s'agira pas de REST API ni de GraphQL ! Nous allons devoir nous servir des RPC (Remote Procedure Call).
Ainsi, je vais vous expliquer comment je m'y prends pour uploader des ressources publiques. Notez que le mot public a son importance étant donné que pour une ressource publique, il suffit de le positionner dans un dossier public/ à la racine de votre projet Meteor.
Ce fichier sera accessible via un appel HTTP standard. Nous verrons ultérieurement comment déposer les fichiers dans un dossiers inaccessible, nous forçant ainsi à gérer les accès aux fichiers (plus facile à faire une fois que nous avons mis en place des sessions !).
Lorsque vous travaillez en Typescript, votre code est compilé. Donc au runtime, votre interpreter se positionne dans le dossier .meteor qui contient le build du projet. Ce qui signifie que parcourir les dossiers et fichiers depuis cet endroit peut s'avérer fastidieux !
C'est pourquoi il y a des variables d'environnement pour nous aider à nous repérer plus facilement au niveau des paths. Nous utiliserons : process.env.PWD
Commençons par créer un dossier /public à la racine de notre projet et dans ce même dossier, ajoutons un fichier .keep pour pouvoir ensuite faire les règles .gitignore nécessaires :
# gitignore
public/
!public/.keep
Maintenant tout ce que vous uploadez ne sera pas versionné (évitons d'envoyer des fichiers binaires sur git... 😅).
// /imports/api/file.ts
import { Meteor } from 'meteor/meteor'
import * as fs from 'fs'
import path from 'path'
import { v4 as uuid } from 'uuid'
const FILES_PATH = path.resolve(process.env.PWD ?? '', 'public/files')
type UploadedFile = {
mimeType: string
name: string
}
Meteor.methods({
'file.upload'(
file: UploadedFile, // Changement de type car le File Object ne passe pas via RPC
fileData: any // Ici nous passons les données binaires en raw
) {
if (Meteor.isServer) {
// On donne un nom unique à notre fichier uploadé
const fileName = uuid()
const filePath = path.resolve(FILES_PATH, `${fileName}.${file.mimeType?.split('/')?.[1]}`)
// On écrit notre fichier dans notre répertoire
fs.writeFileSync(filePath, fileData, 'binary')
// ... Ici je fais les écritures DB nécessaires (à personnaliser)
// On retourne le nom du fichier uploadé
return `${fileName}.${file.mimeType?.split('/')?.[1]}`
}
},
})
Mise à jour du fichier api/index.ts :
// imports/api/index.ts
// ...
import './file'
// ...
Sur notre page, nous allons ajouter un input file pour gérer l'envoi du fichier :
// pages/page.tsx
//...
type MaPageProps = {
uploadFile: (f: File) => void
}
const MaPage = ({ uploadFile }: MaPageProps) => {
return (
/* ... form inputs */
<div>
<label htmlFor="name">
Photo
</label>
<input type="file" onChange={e => uploadFile(e.currentTarget.files?.[0])} />
</div>
)
}
export default withTracker(() => {
return {
uploadFile: (file: File) => {
if (file) {
const reader = new FileReader()
reader.onload = function () {
Meteor.call(
'file.upload',
// File to Object pour le RPC
{
mimeType: file.type,
name: file.name,
},
// Raw binary data
reader.result,
(err: any, fileName: string) => {
if (err) {
console.log(err)
return error()
}
alert('Fichier uploadé')
console.log(fileName)
}
)
}
reader.readAsBinaryString(file)
}
}
}
})(MaPage)
Une fois le fichier uploadé, puisqu'il s'agit d'un fichier public. Je pars du principe que vous avez fait les stockage DB nécessaires. Une fois que vous récupérez votre collection via des requêtes, il ne vous reste plus qu'à l'afficher. Vous pouvez tester avec une URL en dur juste pour vérifier que votre fichier est accessible.
Si votre dossier public est organisé de la sorte :
/public
|_/img
|_picture.jpg
Il ne vous reste plus qu'à faire un essai :
// /pages/index.tsx
const pictureUrl = "/img/picture.jpg";
// ...
<img src={pictureUrl} />
Dans un prochain article on parlera de la pagination de données sur les requêtes de collection. D'ici là, portez vous bien, bon code ! 😉
Développeur passionné depuis plus de 20 ans, j'ai une appétence particulière pour les défis techniques et changer de technologie ne me fait pas froid aux yeux.
Nous utilisons des cookies sur ce site pour améliorer votre expérience d'utilisateur.