上传图片 - Nodejs Paperclip 和 S3

8

我希望使用NodeJS上传图像并保存到用户记录,就像Rails的Paperclip gem一样。

我认为应该按照以下步骤进行,但我对此包的工作原理感到非常困惑:

  1. 接收图像并通过Paperclip进行大小调整
  2. 保存或更新到S3
  3. 将文件保存到数据库中的user

我有一个Rails Postgres数据库,用户可以上传存储在S3中并由Paperclip gem重新格式化的图像。以下是它的存储方式:

irb(main):003:0> user.avatar
=> #<Paperclip::Attachment:0x000055b3e043aa50 @name=:avatar, 
@name_string="avatar", @instance=#<User id: 1, email: 
"example@gmail.com", created_at: "2016-06-11 22:52:36", 
updated_at: "2019-06-16 17:17:16", first_name: "Clarissa", 
last_name: "Jones", avatar_file_name: "two_people_talking.gif", 
avatar_content_type: "image/gif", avatar_file_size: 373197, 
avatar_updated_at: "2019-06-16 17:17:12", @options={:convert_options=>{}, 
:default_style=>:original, :default_url=>":style/missing.png", 
:escape_url=>true, :restricted_characters=>/[&$+,\/:;=?@<>\[\]\ 
{\}\|\\\^~%# ]/, :filename_cleaner=>nil, 
:hash_data=>":class/:attachment/:id/:style/:updated_at", 
:hash_digest=>"SHA1", :interpolator=>Paperclip::Interpolations, 
:only_process=>[], 
:path=>"/:class/:attachment/:id_partition/:style/:filename", 
:preserve_files=>false, :processors=>[:thumbnail], 
:source_file_options=>{:all=>"-auto-orient"}, :storage=>:s3, 
:styles=>{:large=>"500x500#", :medium=>"200x200#", 
:thumb=>"100x100#"}, :url=>":s3_path_url", 
:url_generator=>Paperclip::UrlGenerator, 
:use_default_time_zone=>true, :use_timestamp=>true, :whiny=>true, 
:validate_media_type=>true, :adapter_options=> 
{:hash_digest=>Digest::MD5}, 
:check_validity_before_processing=>true, :s3_host_name=>"s3-us- 
west-2.amazonaws.com", :s3_protocol=>"https", :s3_credentials=> 
{:bucket=>"example", :access_key_id=>"REDACTED", 
:secret_access_key=>"REDACTED", 
:s3_region=>"us-west-2"}}, @post_processing=true, 
@queued_for_delete=[], @queued_for_write={}, @errors={}, 
@dirty=false, @interpolator=Paperclip::Interpolations, 
@url_generator=#<Paperclip::UrlGenerator:0x000055b3e043a8e8 
@attachment=#<Paperclip::Attachment:0x000055b3e043aa50 ...>>, 
@source_file_options={:all=>"-auto-orient"}, @whiny=true, 
@s3_options={}, @s3_permissions={:default=>:"public-read"}, 
@s3_protocol="https", @s3_metadata={}, @s3_headers={}, 
@s3_storage_class={:default=>nil}, 
@s3_server_side_encryption=false, @http_proxy=nil, 
@use_accelerate_endpoint=nil>

user.avatar(:thumb) 返回:

https://s3-us-west-2.amazonaws.com/example/users/avatars/000/000/001/thumb/two_people_talking.gif?1560705432

现在,我正在尝试允许用户通过react-native应用程序上传新的/更改图像,后端是Nodejs,这对我来说相对较新。

我非常困惑如何实现这一点,特别是因为所有示例都引用了我没有使用的Mongoose。

为了展示如何成功更新用户,这里是如何更新用户的first_name

users.updateUserPhoto = (req, res) => {

  let id = req.decoded.id
  let first_name = req.body.first_name

  models.Users.update(
      first_name: first_name,
      {
        where: {
          id: req.decoded.id
        }
      },
    ).then(response => {
        res.status(200).json({ status: 200, data: { response } });
    })
    .catch(error => {
        res.status(500).json({ status: 500, err: error });
    })
}

我找到了这个包node-paperclip-s3,这是我要做的事情:

'use strict'
let users = {};
const { Users } = require('../models');
let models = require("../models/index");

let Sequelize = require('sequelize');
let Paperclip = require('node-paperclip');
let Op = Sequelize.Op;
let sequelizeDB = require('../modules/Sequelize');

users.updateUserPhoto = (req, res) => {
  let id = req.decoded.id
  let avatar = req.body.avatar <- this is a file path

  models.Users.plugin(Paperclip.plugins, {
      avatar: {
        styles: [
          { original: true },
          { large:     { width: 500,  height: 500 } },
          { medium:    { width: 200, height: 200 } },
          { thumb:  { width: 100, height: 100 } }
        ],
        prefix:      '/users/{{attachment}}/{{id}}/{{filename}}',
        name_format: '{{style}}.{{extension}}',
        storage: 's3',
        s3: {
          bucket: process.env.S3_BUCKET_NAME,
          region: 'us-west-2',
          key: process.env.AWS_ACCESS_KEY_ID,
          secret: process.env.AWS_SECRET_ACCESS_KEY,
        }
      }
  })

  models.Users.update(
      avatar,
      {
        where: {
          id: req.decoded.id
        }
      },
    ).then(response => {
        res.status(200).json({ status: 200, data: { response } });
    })
    .catch(error => {
        res.status(500).json({ status: 500, err: error });
    })
}

我也尝试过类似以下的方式:

    models.Users.update(Paperclip.plugins, {
      avatar: {
        styles: [
          { original: true },
          { large:     { width: 500,  height: 500 } },
          { medium:    { width: 200, height: 200 } },
          { thumb:  { width: 100, height: 100 } }
        ],
        prefix:      '/users/{{attachment}}/{{id}}/{{filename}}',
        name_format: '{{style}}.{{extension}}',
        storage: 's3',
        s3: {
          bucket: process.env.S3_BUCKET_NAME,
          region: 'us-west-2',
          key: process.env.AWS_ACCESS_KEY_ID,
          secret: process.env.AWS_SECRET_ACCESS_KEY,
        }
      },
      {
        where: {
          id: req.decoded.id
        }
      },
    ).then(response => {
        res.status(200).json({ status: 200, data: { response } });
    })
    .catch(error => {
        res.status(500).json({ status: 500, err: error });
    })
  })

我已经尝试过:
let new_avatar = (Paperclip.plugins, {
      avatar: {
        styles: [
          { original: true },
          { large:     { width: 500,  height: 500 } },
          { medium:    { width: 200, height: 200 } },
          { thumb:  { width: 100, height: 100 } }
        ],
        prefix:      `/users/avatars/{{attachment}}/{{id}}/{{filename}}`,
        name_format: '{{style}}.{{extension}}',
        storage: 's3',
        s3: {
          bucket: process.env.S3_BUCKET_NAME,
          region: 'us-west-2',
          key: process.env.AWS_ACCESS_KEY_ID,
          secret: process.env.AWS_SECRET_ACCESS_KEY,
        }
      },
  })

  let data = {
    avatar: new_avatar
  }

  models.Users.update(
      data,
      {
        where: {
          id: req.decoded.id
        }
      },
    ).then(response => {
        res.status(200).json({ status: 200, data: { response } });
    })
    .catch(error => {
        res.status(500).json({ status: 500, err: error });
    })

从上面链接中的示例中,我不理解它是如何保存到S3的,也不知道它是如何以与Rails gem创建记录相同的方式更新数据库的。

问题:如何以与Rails paperclip gem相同的方式保存调整大小后的图像+原始图像到S3和user记录中的数据库。

我最初为此打开了一个400分的赏金,并非常乐意向任何帮助我解决这个问题的人提供400分的奖励。谢谢!


那么你的后端仍然是Rails,UI部分是React Native吗? - Tarun Lalwani
@Tarun Lalwami 不,这个移动应用程序有一个NodeJS API,前端是React Native。但现有的Web应用程序是Ruby on Rails,这就是为什么使用paperclip来设置用户图像的原因。 - gwalshington
有错误吗?我猜 models.Users.plugin 是未定义的? - 鄭脈龍
1
node-paperclip 的作者表示:“它目前只适用于 mongoose,但已经设置好了以便轻松扩展到其他数据库。” [repo] (https://github.com/ballantyne/node-paperclip#node-paperclip) - 鄭脈龍
是的,未定义。我尝试了几种方法,但基本上无论以何种格式进行调整大小都不起作用。感谢您阅读了我没有阅读的内容 - 我想知道“轻松扩展”是什么意思?我也没有承诺使用这个软件包 - 基本上我只需要完成调整大小并像它们当前存储的那样保存。您知道我还能用其他方法实现吗?谢谢! - gwalshington
1个回答

0
以下代码适用于Node.js。 我已经添加了一个API,用于将前端的图像保存到AWS S3。
我在代码中添加了注释以便更好地理解。
var express = require("express");
var router = express.Router();
var aws = require('aws-sdk');
aws.config.update({
    secretAccessKey: config.AwsS3SecretAccessKey,
    accessKeyId: config.AwsS3AccessKeyId,
    region: config.AwsS3Region
});

router
    .route("/uploadImage")
    .post(function (req, res) {

    //req.files.imageFile contains the file from client, modify it as per you requirement
    var file = getDesiredFileFromPaperclip(req.files.imageFile);
    const fileName = new Date().getTime() + file.name;

    //before uploading, we need to create an instance of client file
    file.mv(fileName, (movErr, movedFile) => {
        if (movErr) {
            console.log(movErr);
            res.send(400);
            return;
        }
        //read file data
        fs.readFile(fileName, (err, data) => {
            if (err) {
                console.error(err)
                res.send(400);
            }
            else {

                //as we have byte data of file, delete the file instance
                try {
                    fs.unlink(fileName);
                } catch (error) {
                    console.error(error);
                }

                //now, configure aws

                var s3 = new aws.S3();
                const params = {
                    Bucket: config.AwsS3BucketName, // pass your bucket name
                    Key: fileName, // file will be saved as bucket_name/file.ext
                    Body: data
                }

                //upload file
                s3.upload(params, function (s3Err, awsFileData) {
                    if (s3Err) {
                        console.error(s3Err)
                        res.send(400);
                    } else {
                        console.log(`File uploaded successfully at ${awsFileData.Location}`)

                        //update uploaded file data in database using 'models.Users.update'

                        //send response to client/frontend
                        var obj = {};
                        obj.status = { "code": "200", "message": "Yipee!! Its Done" };
                        obj.result = { url: awsFileData.Location };
                        res.status(200).send(obj);
                    }
                });
            }
        });
    });
});

这是老派的、不花哨的解决方案。请试用一下并让我知道结果。


嘿 - 太棒了 - 绝对不需要任何花哨的东西。getDesiredFileFromPaperclip 函数是什么?我认为这只是上传到 S3?问题在于:1.将图像以 Paperclip 已经保存的那种长格式保存到数据库中。2.有许多不同的尺寸,因此在 S3 中,每个尺寸都有多个文件夹。在保存之前,我需要将图像调整为多个尺寸。例如,这是缩略图图像的路径 https://s3-us-west-2.amazonaws.com/example/users/avatars/000/000/001/thumb/two_people_talking.gif?1560705432 - gwalshington
getDesiredFileFromPaperclip函数从paperclip返回图像文件;您可以在其中编写与图像相关的代码。如代码中所述,您可以将图像保存为字符串(尽管我认为保存从S3获取的图像链接更好)。我不认为保存多个大小相同的图像是一个好选择,在返回图像之前添加一个调整图像大小的代码。 - dd619
我感谢你的帮助,但是你的回答没有解决我整个问题。这个问题比仅仅上传图像到S3复杂得多。 - gwalshington

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接