无法通过nodejs在aws s3上上传图像并使用预签名的URL进行抖动

时间:2019-01-31 20:55:58

标签: node.js amazon-s3 flutter

我试图用带dart / flutter的aws s3存储桶(带有预签名网址)通过Node.js上传图像。我成功生成了预签名的url并将其发送回前端,但无法通过s3发出带有文件的放置请求。如果我正在打印状态码,则得到400。

 import 'dart:io';
import 'dart:convert';
import 'package:path/path.dart';
import 'package:async/async.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:mime/mime.dart';
import '../global.dart';

class AddAccount extends StatelessWidget {
  Widget build(context) {
    return Scaffold(
      appBar: AppBar(
        iconTheme: IconThemeData(
          color: Colors.grey
        ),
        title: Text('Add Account', style: TextStyle(color: Colors.black54, fontSize: 20.0)),
        backgroundColor: Colors.white,
      ),
      body: addAccountBody(context),
    );
  }
  Widget addAccountBody(context){
    return ListView(
      children: <Widget>[
        TextField(
          decoration: InputDecoration(
            labelText: 'email'
          ),
          textCapitalization: TextCapitalization.characters,
        ),

        TextField(
          decoration: InputDecoration(
            labelText: 'name'
          ),
          textCapitalization: TextCapitalization.characters,
        ),
        OutlineButton(
          onPressed: (){
            openImagePicker(context);
          },
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Icon(Icons.camera_alt),
              Text('add image')
            ],
          ),
        )
      ],
    );
  }
  void openImagePicker(BuildContext context){
    showModalBottomSheet(
      context: context,
      builder: (BuildContext context) {
        return Container(
          height: 80.0,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              IconButton(
                onPressed: (){
                  getImage(context, ImageSource.camera);
                },
                icon: Icon(Icons.camera_alt),
              ),
              SizedBox(width: 20.0,),
              IconButton(
                onPressed: () async {
                  File image = await getImage(context, ImageSource.gallery);
                  http.Response response = await http.get('$url1/api/upload', headers: {
                    "x-auth" : 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1YzQ0M2M0NDE0NjkxODM3ZTQxNTMxZTgiLCJhY2Nlc3MiOiJhdXRoIiwiaWF0IjoxNTQ3OTc1NzQ5fQ.73_tyhu3vTpsnMncmjTS0xDBec08pScxpa35yRlLuzQ'
                  });
                  Map<String, dynamic> uploadUrl = json.decode(response.body);
                  final dataType = lookupMimeType(image.path).split("/");
                  final imageUploadRequest = http.MultipartRequest("PUT", Uri.parse(uploadUrl['url']));
                  final file = await http.MultipartFile.fromPath("${uploadUrl['key']}", image.path, contentType: MediaType(dataType[0], dataType[1]));
                  imageUploadRequest.files.add(file);
                  imageUploadRequest.fields['user'] = Uri.encodeComponent('user');
                  imageUploadRequest.headers['Content-Type'] = 'image/jpeg';
                  print(file.contentType);
                  try{
                    final streamedResponse = await imageUploadRequest.send();
                    final res = await http.Response.fromStream(streamedResponse);
                     print(res.statusCode);
                  }catch(e){
                    print(e);
                  }
                },
                icon: Icon(Icons.camera),
              ),

            ],
          ),
        );
      }
    ); 
  }
  Future<File> getImage(BuildContext context, ImageSource sourse) async {
    File file = await ImagePicker.pickImage(source: sourse, maxWidth: 400.0);
    Navigator.pop(context);
    return file;

  }
}

颤动代码

    const AWS = require('aws-sdk');
const uuid = require('uuid/v1');
const keys = require('../config/keys');
const { authenticate } = require('../middleware/authenticate')

const s3 = new AWS.S3({
    accessKeyId: keys.accessKeyId,
    secretAccessKey: keys.secretAccessKey
});

module.exports = (app) => {
    app.get('/api/upload', authenticate, (req,res) => {
        console.log(req.user.id);
        const key = `${req.user.id}/${uuid()}.jpeg`;
        s3.getSignedUrl('putObject', {
            Bucket: 'flutter-bucket',
            ContentType: 'image/jpeg',
            Key: key
        }, (err, url) => {
            res.send({key, url});
        })
    });
}

节点代码

1 个答案:

答案 0 :(得分:0)

S3预设URL需要原始字节作为主体,而不是多部分文件。因此,我使用了以下代码:

List<int> content = await file.readAsBytes();

var streamed = http.StreamedRequest("PUT", Uri.parse(url));
streamed.headers["Content-Type"] = mimeType;
streamed.headers["Content-Length"] = "${content.length}";
streamed.sink.add(content);
streamed.sink.close();

var response = await streamed.send();