Browse Source

refactor(asset-server-plugin): Change the way files are served

This change makes it possible to serve files from any source (not just local file system) so that any valid AssetStorageStrategy should be compatible with the plugin.
Michael Bromley 5 years ago
parent
commit
f75fbed783

+ 1 - 0
packages/asset-server-plugin/package.json

@@ -30,6 +30,7 @@
     "typescript": "3.8.3"
     "typescript": "3.8.3"
   },
   },
   "dependencies": {
   "dependencies": {
+    "file-type": "^14.3.0",
     "fs-extra": "^9.0.0",
     "fs-extra": "^9.0.0",
     "sharp": "0.25.2"
     "sharp": "0.25.2"
   }
   }

+ 42 - 14
packages/asset-server-plugin/src/plugin.ts

@@ -13,6 +13,7 @@ import {
 } from '@vendure/core';
 } from '@vendure/core';
 import { createHash } from 'crypto';
 import { createHash } from 'crypto';
 import express, { NextFunction, Request, Response } from 'express';
 import express, { NextFunction, Request, Response } from 'express';
+import { fromBuffer } from 'file-type';
 import fs from 'fs-extra';
 import fs from 'fs-extra';
 import { Server } from 'http';
 import { Server } from 'http';
 import path from 'path';
 import path from 'path';
@@ -188,10 +189,6 @@ export class AssetServerPlugin implements OnVendureBootstrap, OnVendureClose {
         fs.ensureDirSync(cachePath);
         fs.ensureDirSync(cachePath);
         this.createAssetServer();
         this.createAssetServer();
         const { hostname, port } = AssetServerPlugin.options;
         const { hostname, port } = AssetServerPlugin.options;
-        // console.log('SWAGGGGGGGG!');
-        // this.healthCheckRegistryService.registerIndicatorFunction(() =>
-        //     this.dns.pingCheck('asset-server', `${hostname}:${port}`),
-        // );
     }
     }
 
 
     /** @internal */
     /** @internal */
@@ -209,7 +206,7 @@ export class AssetServerPlugin implements OnVendureBootstrap, OnVendureClose {
         assetServer.get('/health', (req, res) => {
         assetServer.get('/health', (req, res) => {
             res.send('ok');
             res.send('ok');
         });
         });
-        assetServer.use(this.serveStaticFile(), this.generateTransformedImage());
+        assetServer.use(this.sendAsset(), this.generateTransformedImage());
 
 
         this.server = assetServer.listen(AssetServerPlugin.options.port, () => {
         this.server = assetServer.listen(AssetServerPlugin.options.port, () => {
             const addressInfo = this.server.address();
             const addressInfo = this.server.address();
@@ -224,15 +221,24 @@ export class AssetServerPlugin implements OnVendureBootstrap, OnVendureClose {
     }
     }
 
 
     /**
     /**
-     * Sends the file requested to the broswer.
+     * Reads the file requested and send the response to the browser.
      */
      */
-    private serveStaticFile() {
-        return (req: Request, res: Response) => {
-            const filePath = path.join(
-                AssetServerPlugin.options.assetUploadDir,
-                this.getFileNameFromRequest(req),
-            );
-            res.sendFile(filePath);
+    private sendAsset() {
+        return async (req: Request, res: Response, next: NextFunction) => {
+            const key = this.getFileNameFromRequest(req);
+            try {
+                const file = await AssetServerPlugin.assetStorage.readFileToBuffer(key);
+                let mimeType = this.getMimeType(key);
+                if (!mimeType) {
+                    mimeType = (await fromBuffer(file))?.mime || 'application/octet-stream';
+                }
+                res.contentType(mimeType);
+                res.send(file);
+            } catch (e) {
+                const err = new Error('File not found');
+                (err as any).status = 404;
+                return next(err);
+            }
         };
         };
     }
     }
 
 
@@ -243,7 +249,7 @@ export class AssetServerPlugin implements OnVendureBootstrap, OnVendureClose {
      */
      */
     private generateTransformedImage() {
     private generateTransformedImage() {
         return async (err: any, req: Request, res: Response, next: NextFunction) => {
         return async (err: any, req: Request, res: Response, next: NextFunction) => {
-            if (err && err.status === 404) {
+            if (err && (err.status === 404 || err.statusCode === 404)) {
                 if (req.query) {
                 if (req.query) {
                     Logger.debug(`Pre-cached Asset not found: ${req.path}`, loggerCtx);
                     Logger.debug(`Pre-cached Asset not found: ${req.path}`, loggerCtx);
                     let file: Buffer;
                     let file: Buffer;
@@ -309,4 +315,26 @@ export class AssetServerPlugin implements OnVendureBootstrap, OnVendureClose {
         const dirName = path.dirname(fileName);
         const dirName = path.dirname(fileName);
         return path.join(dirName, `${baseName}${suffix}${ext}`);
         return path.join(dirName, `${baseName}${suffix}${ext}`);
     }
     }
+
+    /**
+     * Attempt to get the mime type from the file name.
+     */
+    private getMimeType(fileName: string): string | undefined {
+        const ext = path.extname(fileName);
+        switch (ext) {
+            case 'jpg':
+            case 'jpeg':
+                return 'image/jpeg';
+            case 'png':
+                return 'image/png';
+            case 'gif':
+                return 'image/gif';
+            case 'svg':
+                return 'image/svg+xml';
+            case 'tiff':
+                return 'image/tiff';
+            case 'webp':
+                return 'image/webp';
+        }
+    }
 }
 }