我试图用带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});
})
});
}
节点代码
答案 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();