上传图片 - Nodejs Paperclip 和 S3

Posted

技术标签:

【中文标题】上传图片 - Nodejs Paperclip 和 S3【英文标题】:Upload images - Nodejs Paperclip and S3 【发布时间】:2019-11-24 14:47:32 【问题描述】:

我想将图像上传到 S3 并使用 NodeJS 保存到用户记录,就像 Rails Paperclip gem 一样。

我相信这应该是这个过程,但我对这个包应该如何工作感到很困惑:

    接收图像并通过回形针调整大小 保存或更新到 S3 将文件保存到数据库中的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 回形针 gem 保存到 S3 和数据库中的 user 记录完全相同的方式保存调整大小的图像 + 原始图像。

我最初为 400 分的赏金开放了这个,我很高兴仍然向任何可以帮助我解决这个问题的人提供 400 分。谢谢!!

【问题讨论】:

所以你的后端仍然是 rails 并且 UI 部分是 react native? @Tarun Lalwami 不,这个移动应用程序有一个 NodeJS API,前端是 react native。但是现有的网络应用是 Ruby on Rails,这就是为什么使用回形针来设置用户的图像。 有什么错误吗?我猜models.Users.plugin 是未定义的? node-paperclip 的作者说“它目前仅适用于猫鼬,但设置为可以轻松扩展以适用于其他数据库。”repo跨度> 是的,未定义。我已经尝试了几件事,基本上调整大小并没有以任何格式进行。感谢您阅读我没有阅读的地方 - 我想知道“轻松扩展”是什么意思?我也没有致力于这个包 - 基本上我只需要完成调整大小并像当前存储它们一样保存。你知道我可以做到这一点的任何其他方法吗?谢谢!! 【参考方案1】:

以下代码适用于 nodeJs。 我添加了一个 api 来将图像从前端保存到 AWS S3。

我在代码中添加了 cmets 以便更好地理解。

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. 以回形针已经保存的那种长格式将图像保存在数据库中。 2. 有很多不同的尺寸,所以在S3中,每个尺寸都有多个文件夹。我需要在保存之前将图像调整为多种尺寸。例如,这里是缩略图的路径https://s3-us-west-2.amazonaws.com/example/users/avatars/000/000/001/thumb/two_people_talking.gif?1560705432 getDesiredFileFromPaperclip 函数从回形针返回图像文件;您可以在其中编写与图像相关的代码。正如代码中所述,您可以将图像保存为字符串(尽管我认为保存从 S3 获得的图像链接更好)。我不认为保存具有多个尺寸的相同图像是不错的选择,在返回之前添加代码以调整图像大小。 感谢您的帮助,但您的回答并没有解决我的整个问题。这个问题比将图像上传到 S3 要复杂得多。

以上是关于上传图片 - Nodejs Paperclip 和 S3的主要内容,如果未能解决你的问题,请参考以下文章

Rails:Amazon S3 + Paperclip 不在数据库中存储任何内容?

Paperclip + Rails 与负载平衡机器

Paperclip S3 - 可以上传图像但无法查看它们

Rails:Paperclip 为相似的视频产生不同的结果

添加了图像上传进度条(Paperclip、Javascript),但图像本身没有被保存。我怎样才能解决这个问题?

如何使用 Mongoose、Express、Angular 4 和 NodeJS 上传/保存和显示图片