J'ai eu du mal à trouver des exemples clairs sur la façon de télécharger et de télécharger des fichiers dans une application mobile basée sur Expo. Pour aider les autres confrontés au même défi – ou toute personne simplement curieuse – j'ai écrit cet article.
En cours de route, nous explorerons les concepts clés qu'il est utile de comprendre :
Ce que nous couvrirons :
Tous les codes et la collection Postman sont disponibles dans mon GitHub.
Le serveur fonctionne sur Fastify (une version modernisée d'Express.js). Pour démarrer l'application, procédez comme suit :
Dans app.js, nous avons trois points de terminaison clés :
fastify.get("/download", async function handler(_, reply) { const fd = await open(FILE_TO_DOWNLOAD); const stream = fd.createReadStream(); const mimeType = mime.lookup(FILE_TO_DOWNLOAD); console.log(`Downloading -> ${FILE_TO_DOWNLOAD}`); return reply .type(mimeType) .header( "Content-Disposition", `attachment; filename=${path.basename(FILE_TO_DOWNLOAD)}` ) .send(stream); });
Ce point de terminaison envoie example.webp sous forme de flux à l'aide de createReadStream(). Le type MIME est inclus afin que le client sache comment gérer le fichier. Par exemple.webp, ce sera image/webp.
?Remarque : Le type MIME définit le format du fichier envoyé. Cela aide le client à l'afficher correctement.
Voir plus de types MIME.
L'en-tête Content-Disposition définit comment le contenu doit être présenté au client. Y compris la pièce jointe ; nom de fichier=
En savoir plus sur la disposition du contenu
fastify.post("/upload-multiples", async function handler(request) { const parts = request.files(); const uploadResults = []; for await (const file of parts) { const fileBuffer = await file.toBuffer(); const filename = file.filename; const filePath = path.join(DIR_TO_UPLOAD, filename); await writeFile(filePath, fileBuffer); uploadResults.push({ filename, uploaded: true }); console.log(`Uploaded -> ${filePath}`); } return { uploadedFiles: uploadResults }; });
Ce point de terminaison accepte une requête multipart/form-data. Il :
Par exemple, une demande pourrait ressembler à ceci :
fastify.get("/download", async function handler(_, reply) { const fd = await open(FILE_TO_DOWNLOAD); const stream = fd.createReadStream(); const mimeType = mime.lookup(FILE_TO_DOWNLOAD); console.log(`Downloading -> ${FILE_TO_DOWNLOAD}`); return reply .type(mimeType) .header( "Content-Disposition", `attachment; filename=${path.basename(FILE_TO_DOWNLOAD)}` ) .send(stream); });
Ce point de terminaison attend un seul fichier binaire dans le corps de la requête (application/octet-stream). Contrairement à multipart/form-data, le fichier est déjà une donnée binaire, nous pouvons donc l'écrire directement sur le disque.
La demande ressemblera à ceci dans Postman :
Pour exécuter l'application :
Toutes les fonctionnalités de l'application Web sont contenues dans App.tsx :
Cette application React offre trois fonctionnalités clés :
fastify.post("/upload-multiples", async function handler(request) { const parts = request.files(); const uploadResults = []; for await (const file of parts) { const fileBuffer = await file.toBuffer(); const filename = file.filename; const filePath = path.join(DIR_TO_UPLOAD, filename); await writeFile(filePath, fileBuffer); uploadResults.push({ filename, uploaded: true }); console.log(`Uploaded -> ${filePath}`); } return { uploadedFiles: uploadResults }; });
Lorsque l'utilisateur clique sur le bouton "Télécharger", l'application :
Le comportement dépend de l'en-tête Content-Disposition renvoyé par le serveur :
Pour déclencher le téléchargement, l'application crée un fichier avec le href défini sur l'objectURL et clique dessus par programme, simulant une action de téléchargement de l'utilisateur.
fastify.post("/upload-octet-stream", async function handler(request) { const filename = request.headers["x-file-name"] ?? "unknown.text"; const data = request.body; const filePath = path.join(DIR_TO_UPLOAD, filename); await writeFile(filePath, data); return { uploaded: true }; });
Lorsque le bouton « Télécharger le fichier » est cliqué :
Cela permet au serveur de gérer et d'enregistrer correctement les fichiers téléchargés.
const downloadFile = async () => { const response = await fetch(DOWNLOAD_API); if (!response.ok) throw new Error("Failed to download file"); const blob = await response.blob(); const contentDisposition = response.headers.get("Content-Disposition"); const isInline = contentDisposition?.split(";")[0] === "inline"; const filename = contentDisposition?.split("filename=")[1]; const url = window.URL.createObjectURL(blob); if (isInline) { window.open(url, "_blank"); } else { const a = document.createElement("a"); a.href = url; a.download = filename || "file.txt"; a.click(); } window.URL.revokeObjectURL(url); };
Cette approche est plus simple que d'utiliser multipart/form-data : envoyez simplement le fichier directement dans le corps de la requête sous forme de données binaires et incluez le nom du fichier dans les en-têtes de la requête.
Vous pouvez démarrer l'application avec ce qui suit :
La logique principale est dans App.tsx où elle restitue ce qui suit :
fastify.get("/download", async function handler(_, reply) { const fd = await open(FILE_TO_DOWNLOAD); const stream = fd.createReadStream(); const mimeType = mime.lookup(FILE_TO_DOWNLOAD); console.log(`Downloading -> ${FILE_TO_DOWNLOAD}`); return reply .type(mimeType) .header( "Content-Disposition", `attachment; filename=${path.basename(FILE_TO_DOWNLOAD)}` ) .send(stream); });
Pour afficher le fichier dans une nouvelle vue (comme lorsque le navigateur ouvre le fichier dans un nouvel onglet), nous devons lire la réponse sous forme de blob, puis la transformer en base64 à l'aide de FileReader.
Nous écrivons le fichier dans le répertoire cache (un répertoire privé auquel seule l'application a accès), puis l'affichons à l'aide d'IntentLauncher ou de Sharing si l'utilisateur utilise iOS.
fastify.post("/upload-multiples", async function handler(request) { const parts = request.files(); const uploadResults = []; for await (const file of parts) { const fileBuffer = await file.toBuffer(); const filename = file.filename; const filePath = path.join(DIR_TO_UPLOAD, filename); await writeFile(filePath, fileBuffer); uploadResults.push({ filename, uploaded: true }); console.log(`Uploaded -> ${filePath}`); } return { uploadedFiles: uploadResults }; });
Ceci est similaire au processus Web mais nous devons lire le blob en base64 à l'aide de FileReader, puis demander l'autorisation de télécharger le fichier là où l'utilisateur souhaite enregistrer le fichier.
fastify.post("/upload-octet-stream", async function handler(request) { const filename = request.headers["x-file-name"] ?? "unknown.text"; const data = request.body; const filePath = path.join(DIR_TO_UPLOAD, filename); await writeFile(filePath, data); return { uploaded: true }; });
Utilisez DocumentPicker pour permettre aux utilisateurs de sélectionner des fichiers, puis utilisez FormData pour ajouter les fichiers sélectionnés à la demande. Le processus est très simple.
const downloadFile = async () => { const response = await fetch(DOWNLOAD_API); if (!response.ok) throw new Error("Failed to download file"); const blob = await response.blob(); const contentDisposition = response.headers.get("Content-Disposition"); const isInline = contentDisposition?.split(";")[0] === "inline"; const filename = contentDisposition?.split("filename=")[1]; const url = window.URL.createObjectURL(blob); if (isInline) { window.open(url, "_blank"); } else { const a = document.createElement("a"); a.href = url; a.download = filename || "file.txt"; a.click(); } window.URL.revokeObjectURL(url); };
Le téléchargement en tant qu'application/octet-stream est encore plus simple que l'utilisation de FormData : définissez les en-têtes avec les détails du fichier et le type de contenu, puis ajoutez le fichier au corps de la requête et c'est tout !
Il peut être un peu déroutant de savoir comment afficher, télécharger et télécharger des fichiers entre plates-formes. Dans cet article, nous avons vu les plus courants.
J'espère vous avoir aidé ?
Suivez-moi sur @twitter
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!