使用 skipper-better-s3 和 sailjs returns 相同密钥的多文件上传

multi file upload with skipper-better-s3 and sailjs returns the same key

如标题所示,我目前正在使用 sailjs + skipper-better-s3 进行 s3 上传。从上传一个效果很好的文件开始,然后因为更改请求需要 multi-file 立即上传所以我添加了一个 for 循环但是通过这样做,所有密钥都将相同并最终成为唯一上传了一个文件,这是最后一个上传的文件,但具有第一个上传文件名。

我确实读过一些文章,人们在说 The problem is because for loop is synchronous and file upload is asynchronous 之类的话,人们说这样做的结果是使用递归,我也尝试过,但运气不好,发生了同样的事情。

我的递归代码如下...

s3_upload_multi: async (req, res) => {
    const generatePath = (rootPath, fieldName) => {
        let path;
        // this is just a switch statement here to check which fieldName is provided then value of path will depend on it
        // as for the other two variable is just checking if upload content type is correct
        return { path };
    };

    const processUpload = async ({
        fieldName,
        awsOp,
        fileExtension,
        rootPath,
        fileName,
    }) => {
        return new Promise(function (resolve, reject) {
            req.file(fieldName).upload(awsOp, async (err, filesUploaded) => {
                if (err) reject(err);
                const filesUploadedF = filesUploaded[0]; // F = first file
                const response = {
                    status: true,
                    errCode: 200,
                    msg: 'OK',
                    response: {
                        url: filesUploadedF.extra.Location,
                        size: filesUploadedF.size,
                        type: fileExtension,
                        filename: filesUploadedF.filename,
                        key: filesUploadedF.extra.Key,
                        field: fieldName,
                    }
                };

                resolve(response);
            });
        });
    }

    const process_recur = async (files, fieldName) => {
        if (files.length <= 0) return;
        const fileUpload = files[0].stream;
        const rootPath = `${sails.config.aws.upload.path.root}`;
        const fileCType = fileUpload.headers['content-type'];
        // console.log(fileCType, 'fileCType');

        const { path } = generatePath(rootPath, fieldName);

        const fileName = fileUpload.filename;

        const fileExtension = fileUpload.filename.split('.').pop();

        const genRan = await UtilsService.genRan(8);
        const fullPath = `${path}${genRan}-${fileName}`;
        const awsOp = {
            adapter: require('skipper-better-s3'),
            key: sails.config.aws.access_key,
            secret: sails.config.aws.secret_key,
            saveAs: fullPath,
            bucket: sails.config.aws.bucket,
            s3params: {
                ACL: 'public-read'
            },
        };

        const config = {
            fieldName,
            awsOp,
            fileExtension,
            rootPath,
            fileName,
        }

        const procceed = await processUpload(config);
        files.shift();
        await process_recur(files, fieldName);
    };

    try {
        const fieldName = req._fileparser.upstreams[0].fieldName;
        const files = req.file(fieldName)._files;
        await process_recur(files, fieldName);
    } catch (e) {
        console.log(e, 'inside UploadService');
        return false;
    }
}

下面是我使用 for 循环的代码,虽然与上面的代码非常相似

s3_upload_multi: async (req, res) => {
    const generatePath = (rootPath, fieldName) => {
        let path;
        // this is just a switch statement here to check which fieldName is provided then value of path will depend on it
        // as for the other two variable is just checking if upload content type is correct
        return { path };
    };

    const processUpload = async ({
        fieldName,
        awsOp,
        fileExtension,
        rootPath,
        fileName,
    }) => {
        return new Promise(function (resolve, reject) {
            req.file(fieldName).upload(awsOp, async (err, filesUploaded) => {
                if (err) reject(err);
                const filesUploadedF = filesUploaded[0]; // F = first file
                const response = {
                    status: true,
                    errCode: 200,
                    msg: 'OK',
                    response: {
                        url: filesUploadedF.extra.Location,
                        size: filesUploadedF.size,
                        type: fileExtension,
                        filename: filesUploadedF.filename,
                        key: filesUploadedF.extra.Key,
                        field: fieldName,
                    }
                };

                resolve(response);
            });
        });
    }

    try {
        const fieldName = req._fileparser.upstreams[0].fieldName;
        const files = req.file(fieldName)._files;

        for (const file of files) {
            const fileUpload = file.stream;
            const rootPath = `${sails.config.aws.upload.path.root}`;
            const fileCType = fileUpload.headers['content-type'];
            // console.log(fileCType, 'fileCType');

            const fileName = fileUpload.filename;
            const { path } = generatePath(rootPath, fieldName);



            const fileExtension = fileUpload.filename.split('.').pop();

            // using a variable here because if this is an image, a thumbnail will be created with the same name as the original one
            const genRan = await UtilsService.genRan(8);
            const fullPath = await `${path}${genRan}-${fileName}`;
            const awsOp = {
                adapter: require('skipper-better-s3'),
                key: sails.config.aws.access_key,
                secret: sails.config.aws.secret_key,
                saveAs: fullPath,
                bucket: sails.config.aws.bucket,
                s3params: {
                    ACL: 'public-read'
                },
            };

            const config = {
                fieldName,
                awsOp,
                fileExtension,
                rootPath,
                fileName,
            }

            const procceed = await processUpload(config);
            console.log(procceed, 'procceed');
        }
    } catch (e) {
        console.log(e, 'inside UploadService');
        return false;
    }

}

我犯了哪一部分错误导致了这种行为?当我 console.log

时,我检查了我的路径,文件名也完全正确

提前感谢您的任何建议和帮助。

很久以前我花了很多时间才弄明白这个问题。 特别是您使用的 skipper-better-s3 没有像 skipper 那样总结详细的文档,回头查看 skipper 文档实际上 saveAs 字段不仅需要 string 还有一个 function 然后你可以用它来获取每个文件的文件名和 return 它根据需要所以实际上你甚至不需要使用 resursive 或 for 循环。

例如你的一些代码

const awsOp = {
    adapter: require('skipper-better-s3'),
    key: sails.config.aws.access_key,
    secret: sails.config.aws.secret_key,
    saveAs: (__newFileStream, next) => {
        // generatePath is what you wrote
        // __newFileStream.filename would the filename of each each before uploading
        // the path is pretty much the s3 key which includes your filename too
        const { path } = generatePath(rootPath, __newFileStream.filename, fieldName);
        return next(undefined, path);
    },
    bucket: sails.config.aws.bucket,
    s3params: {
        ACL: 'public-read'
    },
};

skipper 文档 https://www.npmjs.com/package/skipper#customizing-at-rest-filenames-for-uploads