Issues on creating a custom upload provider

System Information
  • Strapi Version: 4.13.3
  • Operating System: Ubuntu 22.04
  • Database: MySQL 8
  • Node Version: 18
  • NPM Version: 9.3.1
  • Yarn Version:

I would like to let end-user upload a CSV. Server parses the file and do something else like sending an email. Since file upload is on POST /api/upload I have to make a provider so as to achieve the needs.

I have been following this guide to create a local provider:

/package.json

"dependencies": {
   ...
    "strapi-provider-upload-stockcheck": "file:providers/strapi-provider-upload-stockcheck"
  },

strapi/providers/strapi-provider-upload-stockcheck/package.json

{
  "name": "strapi-provider-upload-stockcheck",
  "version": "1.0.20",
  "description": "",
  "main": "index.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "y2kbug",
  "license": "ISC",
  "dependencies": {
    "csv-parse": "^5.5.0",
    "fs-extra": "10.0.0"
  }
}

Referencing to the official guide, and these two pages:

strapi/providers/strapi-provider-upload-stockcheck/index.ts

import { pipeline } from 'stream';
import fs, { ReadStream } from 'fs';
import path from 'path';
import fse from 'fs-extra';

const UPLOADS_FOLDER_NAME = 'uploads/stock-check';

export default {
  init(providerOptions) {
    const uploadPath = path.resolve(strapi.dirs.static.public, UPLOADS_FOLDER_NAME);
    if (!fse.pathExistsSync(uploadPath)) {
      throw new Error(
        `The upload folder (${uploadPath}) doesn't exist or is not accessible. Please make sure it exists.`
      );
    }

    return {
      upload(file) {
        console.log('OVERRIDE FUNCTION UPLOAD');
        // upload the file in the provider
        // file content is accessible by `file.buffer`
      },

      uploadStream(file): Promise<void> {
        console.log('OVERRIDE FUNCTION uploadStream');

        if (!file.stream) {
          return Promise.reject(new Error('Missing file stream'));
        }

        const { stream } = file;

        return new Promise((resolve, reject): void => {
          pipeline(
            stream,
            fs.createWriteStream(path.join(uploadPath, `${file.hash}${file.ext}`)),
            (err) => {
              if (err) {
                return reject(err);
              }

              file.url = `/${UPLOADS_FOLDER_NAME}/${file.hash}${file.ext}`;
            }
          );

          resolve();
        });

        const headers = [
          'SKU',
          'Quantity',
        ];

        const records = [];
        const {parse} = require('csv-parse');
        const parser = parse({
          delimiter: ',',
        });

        // Use the readable stream api to consume records
        parser.on('readable', function () {
          let record;
          while ((record = parser.read()) !== null) {
            records.push(record);
          }
        });

        // Catch any error
        parser.on('error', function (err) {
          console.error(err.message);
        });

        // Test that the parsed records matched the expected records
        parser.on('end', function () {
          console.log(records);
        });

        console.log(stream);
        // parser.write(file.stream);

        // Close the readable stream
        parser.end();
      },

      delete(file) {
        console.log('OVERRIDE FUNCTION delete');
        // delete the file in the provider
      },

      checkFileSize(file, {sizeLimit}) {
        // (optional)
        // implement your own file size limit logic
      },

      getSignedUrl(file) {
        // (optional)
        // Generate a signed URL for the given file.
        // The signed URL allows secure access to the file.
        // Only Content Manager assets will be signed.
        // Returns an object {url: string}.
      },

      isPrivate() {
        // (optional)
        // if it is private, file urls will be signed
        // Returns a boolean
      },
    };
  },
};

I have come up with some problems.

  1. all the import lines are problematic:
import { pipeline } from 'stream';
^^^^^^
SyntaxError: Cannot use import statement outside a module
  1. export is problematic:
export default {
^^^^^^
SyntaxError: Unexpected token 'export'
export = {
^^^^^^
SyntaxError: Unexpected token 'export'
export function init(providerOptions) {
^^^^^^
SyntaxError: Unexpected token 'export'
  1. Everytime index.ts is modified, npm install does not update the node_modules. npm update will update but updating everything. If I put npm install && npm update in dockerfile it will consume much time. What is the best way to update?

Thank you very much.

It turns out I can solve it with controller but not provider: