使用 NodeJS 将文件上传到 Amazon S3
Upload a file to Amazon S3 with NodeJS
我 运行 在尝试将文件上传到我的 S3 存储桶时遇到问题。除了我的文件参数似乎不合适外,一切正常。我正在使用 Amazon S3 sdk 从 nodejs 上传到 s3。
这些是我的路线设置:
var multiparty = require('connect-multiparty'),
multipartyMiddleware = multiparty();
app.route('/api/items/upload').post(multipartyMiddleware, items.upload);
这是 items.upload() 函数:
exports.upload = function(req, res) {
var file = req.files.file;
var s3bucket = new AWS.S3({params: {Bucket: 'mybucketname'}});
s3bucket.createBucket(function() {
var params = {
Key: file.name,
Body: file
};
s3bucket.upload(params, function(err, data) {
console.log("PRINT FILE:", file);
if (err) {
console.log('ERROR MSG: ', err);
} else {
console.log('Successfully uploaded data');
}
});
});
};
将 Body
参数设置为 "hello"
之类的字符串可以正常工作。根据 doc,Body
param 必须采用 (Buffer, Typed Array, Blob, String, ReadableStream) 对象数据。 但是,上传文件对象失败并显示以下错误消息:
[Error: Unsupported body payload object]
这是文件对象:
{ fieldName: 'file',
originalFilename: 'second_fnp.png',
path: '/var/folders/ps/l8lvygws0w93trqz7yj1t5sr0000gn/T/26374-7ttwvc.png',
headers:
{ 'content-disposition': 'form-data; name="file"; filename="second_fnp.png"',
'content-type': 'image/png' },
ws:
{ _writableState:
{ highWaterMark: 16384,
objectMode: false,
needDrain: true,
ending: true,
ended: true,
finished: true,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
sync: false,
bufferProcessing: false,
onwrite: [Function],
writecb: null,
writelen: 0,
buffer: [],
errorEmitted: false },
writable: true,
domain: null,
_events: { error: [Object], close: [Object] },
_maxListeners: 10,
path: '/var/folders/ps/l8lvygws0w93trqz7yj1t5sr0000gn/T/26374-7ttwvc.png',
fd: null,
flags: 'w',
mode: 438,
start: undefined,
pos: undefined,
bytesWritten: 261937,
closed: true },
size: 261937,
name: 'second_fnp.png',
type: 'image/png' }
任何帮助将不胜感激!
看来这里有些地方出了问题。根据您的 post,您似乎正在尝试使用 connect-multiparty
中间件支持文件上传。这个中间件所做的是获取上传的文件,将其写入本地文件系统,然后将 req.files
设置为上传的文件。
您的路由配置看起来不错,问题似乎出在您的 items.upload()
函数上。特别是这部分:
var params = {
Key: file.name,
Body: file
};
正如我在回答开头提到的 connect-multiparty
将文件写入本地文件系统,因此您需要打开文件并读取它,然后上传它,然后将其删除本地文件系统。
那是说您可以将您的方法更新为如下所示:
var fs = require('fs');
exports.upload = function (req, res) {
var file = req.files.file;
fs.readFile(file.path, function (err, data) {
if (err) throw err; // Something went wrong!
var s3bucket = new AWS.S3({params: {Bucket: 'mybucketname'}});
s3bucket.createBucket(function () {
var params = {
Key: file.originalFilename, //file.name doesn't exist as a property
Body: data
};
s3bucket.upload(params, function (err, data) {
// Whether there is an error or not, delete the temp file
fs.unlink(file.path, function (err) {
if (err) {
console.error(err);
}
console.log('Temp File Delete');
});
console.log("PRINT FILE:", file);
if (err) {
console.log('ERROR MSG: ', err);
res.status(500).send(err);
} else {
console.log('Successfully uploaded data');
res.status(200).end();
}
});
});
});
};
它所做的是从本地文件系统读取上传的文件,然后将其上传到 S3,然后删除临时文件并发送响应。
这种方法存在一些问题。首先,它的效率不如预期,因为对于大文件,您将在编写文件之前加载整个文件。其次,这个过程不支持大文件的分段上传(我认为在你必须进行分段上传之前,截止是 5 Mb)。
我建议您使用我一直在研究的模块 S3FS which provides a similar interface to the native FS in Node.JS 但抽象出一些细节,例如多部分上传和S3 api(以及添加一些附加功能,如递归方法)。
如果您要引入 S3FS 库,您的代码将如下所示:
var fs = require('fs'),
S3FS = require('s3fs'),
s3fsImpl = new S3FS('mybucketname', {
accessKeyId: XXXXXXXXXXX,
secretAccessKey: XXXXXXXXXXXXXXXXX
});
// Create our bucket if it doesn't exist
s3fsImpl.create();
exports.upload = function (req, res) {
var file = req.files.file;
var stream = fs.createReadStream(file.path);
return s3fsImpl.writeFile(file.originalFilename, stream).then(function () {
fs.unlink(file.path, function (err) {
if (err) {
console.error(err);
}
});
res.status(200).end();
});
};
这将执行的操作是为提供的存储桶和 AWS 凭据实例化模块,然后创建存储桶(如果不存在)。然后,当上传文件的请求通过时,我们将打开文件流,并使用它将文件写入 S3 的指定路径。这将在幕后处理多部分上传片段(如果需要)并且具有通过流完成的好处,因此您不必在开始上传之前等待读取整个文件。
如果您愿意,可以将代码更改为使用事件侦听器从 Promises. Or use the pipe() 方法回调以确定 end/errors。
如果您正在寻找其他方法,请查看 s3fs 的文档,如果您正在寻找其他方法或遇到问题,请随时提出问题。
我发现以下是可行的解决方案::
npm install aws-sdk
安装 aws-sdk 后,使用以下代码在需要的地方替换值。
var AWS = require('aws-sdk');
var fs = require('fs');
var s3 = new AWS.S3();
// Bucket names must be unique across all S3 users
var myBucket = 'njera';
var myKey = 'jpeg';
//for text file
//fs.readFile('demo.txt', function (err, data) {
//for Video file
//fs.readFile('demo.avi', function (err, data) {
//for image file
fs.readFile('demo.jpg', function (err, data) {
if (err) { throw err; }
params = {Bucket: myBucket, Key: myKey, Body: data };
s3.putObject(params, function(err, data) {
if (err) {
console.log(err)
} else {
console.log("Successfully uploaded data to myBucket/myKey");
}
});
});
我在这里找到了有关该主题的完整教程,以供您查找参考 ::
How to upload files (text/image/video) in amazon s3 using node.js
或使用承诺:
const AWS = require('aws-sdk');
AWS.config.update({
accessKeyId: 'accessKeyId',
secretAccessKey: 'secretAccessKey',
region: 'region'
});
let params = {
Bucket: "yourBucketName",
Key: 'someUniqueKey',
Body: 'someFile'
};
try {
let uploadPromise = await new AWS.S3().putObject(params).promise();
console.log("Successfully uploaded data to bucket");
} catch (e) {
console.log("Error uploading data: ", e);
}
var express = require('express')
app = module.exports = express();
var secureServer = require('http').createServer(app);
secureServer.listen(3001);
var aws = require('aws-sdk')
var multer = require('multer')
var multerS3 = require('multer-s3')
aws.config.update({
secretAccessKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
accessKeyId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
region: 'us-east-1'
});
s3 = new aws.S3();
var upload = multer({
storage: multerS3({
s3: s3,
dirname: "uploads",
bucket: "Your bucket name",
key: function (req, file, cb) {
console.log(file);
cb(null, "uploads/profile_images/u_" + Date.now() + ".jpg"); //use
Date.now() for unique file keys
}
})
});
app.post('/upload', upload.single('photos'), function(req, res, next) {
console.log('Successfully uploaded ', req.file)
res.send('Successfully uploaded ' + req.file.length + ' files!')
})
正在将文件上传到 AWS s3 并发送 url 作为访问文件的响应。
Multer 是一个 node.js 中间件,用于处理 multipart/form-data,主要用于上传文件。它写在 busboy 之上以实现最高效率。检查这个 npm 模块 here.
发送请求时,请确保 headers,Content-Type 为 multipart/form-data。
我们在响应中发送文件位置,这将给出 url,但如果你想访问那个 url,请创建存储桶 public 否则你将无法访问它。
upload.router.js
const express = require('express');
const router = express.Router();
const AWS = require('aws-sdk');
const multer = require('multer');
const storage = multer.memoryStorage()
const upload = multer({storage: storage});
const s3Client = new AWS.S3({
accessKeyId: 'your_access_key_id',
secretAccessKey: 'your_secret_access_id',
region :'ur region'
});
const uploadParams = {
Bucket: 'ur_bucket_name',
Key: '', // pass key
Body: null, // pass file body
};
router.post('/api/file/upload', upload.single("file"),(req,res) => {
const params = uploadParams;
uploadParams.Key = req.file.originalname;
uploadParams.Body = req.file.buffer;
s3Client.upload(params, (err, data) => {
if (err) {
res.status(500).json({error:"Error -> " + err});
}
res.json({message: 'File uploaded successfully','filename':
req.file.originalname, 'location': data.Location});
});
});
module.exports = router;
app.js
const express = require('express');
const app = express();
const router = require('./app/routers/upload.router.js');
app.use('/', router);
// Create a Server
const server = app.listen(8080, () => {
console.log("App listening at 8080");
})
感谢 David,他的解决方案帮助我想出了将多部分文件从我的 Heroku 托管站点上传到 S3 存储桶的解决方案。我使用 formidable 来处理传入的表单和 fs 来获取文件内容。希望对您有所帮助。
api.service.ts
public upload(files): Observable<any> {
const formData: FormData = new FormData();
files.forEach(file => {
// create a new multipart-form for every file
formData.append('file', file, file.name);
});
return this.http.post(uploadUrl, formData).pipe(
map(this.extractData),
catchError(this.handleError));
}
}
server.js
app.post('/api/upload', upload);
app.use('/api/upload', router);
upload.js
const IncomingForm = require('formidable').IncomingForm;
const fs = require('fs');
const AWS = require('aws-sdk');
module.exports = function upload(req, res) {
var form = new IncomingForm();
const bucket = new AWS.S3(
{
signatureVersion: 'v4',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: 'us-east-1'
}
);
form.on('file', (field, file) => {
const fileContent = fs.readFileSync(file.path);
const s3Params = {
Bucket: process.env.AWS_S3_BUCKET,
Key: 'folder/' + file.name,
Expires: 60,
Body: fileContent,
ACL: 'public-read'
};
bucket.upload(s3Params, function(err, data) {
if (err) {
throw err;
}
console.log('File uploaded to: ' + data.Location);
fs.unlink(file.path, function (err) {
if (err) {
console.error(err);
}
console.log('Temp File Delete');
});
});
});
// The second callback is called when the form is completely parsed.
// In this case, we want to send back a success status code.
form.on('end', () => {
res.status(200).json('upload ok');
});
form.parse(req);
}
上传-image.component.ts
import { Component, OnInit, ViewChild, Output, EventEmitter, Input } from '@angular/core';
import { ApiService } from '../api.service';
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({
selector: 'app-upload-image',
templateUrl: './upload-image.component.html',
styleUrls: ['./upload-image.component.css']
})
export class UploadImageComponent implements OnInit {
public files: Set<File> = new Set();
@ViewChild('file', { static: false }) file;
public uploadedFiles: Array<string> = new Array<string>();
public uploadedFileNames: Array<string> = new Array<string>();
@Output() filesOutput = new EventEmitter<Array<string>>();
@Input() CurrentImage: string;
@Input() IsPublic: boolean;
@Output() valueUpdate = new EventEmitter();
strUploadedFiles:string = '';
filesUploaded: boolean = false;
constructor(private api: ApiService, public snackBar: MatSnackBar,) { }
ngOnInit() {
}
updateValue(val) {
this.valueUpdate.emit(val);
}
reset()
{
this.files = new Set();
this.uploadedFiles = new Array<string>();
this.uploadedFileNames = new Array<string>();
this.filesUploaded = false;
}
upload() {
this.api.upload(this.files).subscribe(res => {
this.filesOutput.emit(this.uploadedFiles);
if (res == 'upload ok')
{
this.reset();
}
}, err => {
console.log(err);
});
}
onFilesAdded() {
var txt = '';
const files: { [key: string]: File } = this.file.nativeElement.files;
for (let key in files) {
if (!isNaN(parseInt(key))) {
var currentFile = files[key];
var sFileExtension = currentFile.name.split('.')[currentFile.name.split('.').length - 1].toLowerCase();
var iFileSize = currentFile.size;
if (!(sFileExtension === "jpg"
|| sFileExtension === "png")
|| iFileSize > 671329) {
txt = "File type : " + sFileExtension + "\n\n";
txt += "Size: " + iFileSize + "\n\n";
txt += "Please make sure your file is in jpg or png format and less than 655 KB.\n\n";
alert(txt);
return false;
}
this.files.add(files[key]);
this.uploadedFiles.push('https://gourmet-philatelist-assets.s3.amazonaws.com/folder/' + files[key].name);
this.uploadedFileNames.push(files[key].name);
if (this.IsPublic && this.uploadedFileNames.length == 1)
{
this.filesUploaded = true;
this.updateValue(files[key].name);
break;
}
else if (!this.IsPublic && this.uploadedFileNames.length == 3)
{
this.strUploadedFiles += files[key].name;
this.updateValue(this.strUploadedFiles);
this.filesUploaded = true;
break;
}
else
{
this.strUploadedFiles += files[key].name + ",";
this.updateValue(this.strUploadedFiles);
}
}
}
}
addFiles() {
this.file.nativeElement.click();
}
openSnackBar(message: string, action: string) {
this.snackBar.open(message, action, {
duration: 2000,
verticalPosition: 'top'
});
}
}
上传-image.component.html
<input type="file" #file style="display: none" (change)="onFilesAdded()" multiple />
<button mat-raised-button color="primary"
[disabled]="filesUploaded" (click)="$event.preventDefault(); addFiles()">
Add Files
</button>
<button class="btn btn-success" [disabled]="uploadedFileNames.length == 0" (click)="$event.preventDefault(); upload()">
Upload
</button>
上传CSV/Excel
const fs = require('fs');
const AWS = require('aws-sdk');
const s3 = new AWS.S3({
accessKeyId: XXXXXXXXX,
secretAccessKey: XXXXXXXXX
});
const absoluteFilePath = "C:\Project\test.xlsx";
const uploadFile = () => {
fs.readFile(absoluteFilePath, (err, data) => {
if (err) throw err;
const params = {
Bucket: 'testBucket', // pass your bucket name
Key: 'folderName/key.xlsx', // file will be saved in <folderName> folder
Body: data
};
s3.upload(params, function (s3Err, data) {
if (s3Err) throw s3Err
console.log(`File uploaded successfully at ${data.Location}`);
debugger;
});
});
};
uploadFile();
适合我:)
const fileContent = fs.createReadStream(`${fileName}`);
return new Promise(function (resolve, reject) {
fileContent.once('error', reject);
s3.upload(
{
Bucket: 'test-bucket',
Key: `${fileName + '_' + Date.now().toString()}`,
ContentType: 'application/pdf',
ACL: 'public-read',
Body: fileContent
},
function (err, result) {
if (err) {
reject(err);
return;
}
resolve(result.Location);
}
);
});```
使用 aws SDK v3
npm install @aws-sdk/client-s3
上传代码
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
/**
* advisable to save your AWS credentials and configurations in an environmet file. Not inside the code
* AWS lib will automatically load the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY if available in your environment
*/
const s3Client = new S3Client({ region: process.env.AWS_S3_REGION });
/**
* upload a file
* @param file the file object to be uploaded
* @param fileKey the fileKey. could be separated with '/' to nest the file into a folder structure. eg. members/user1/profile.png
*/
export function uploadFile(file, fileKey){
s3Client.send(new PutObjectCommand({
Bucket: process.env.MY_AWS_S3_BUCKET,
Key: fileKey,
Body: file
}));
}
如果你想下载
import { GetObjectCommand } from "@aws-sdk/client-s3";
/**
* download a file from AWS and send to your rest client
*/
app.get('/download', function(req, res, next){
var fileKey = req.query['fileKey'];
var bucketParams = {
Bucket: 'my-bucket-name',
Key: fileKey,
};
res.attachment(fileKey);
var fileStream = await s3Client.send(new GetObjectCommand(bucketParams));
// for TS you can add: if (fileStream.Body instanceof Readable)
fileStream.Body.pipe(res)
});
我 运行 在尝试将文件上传到我的 S3 存储桶时遇到问题。除了我的文件参数似乎不合适外,一切正常。我正在使用 Amazon S3 sdk 从 nodejs 上传到 s3。
这些是我的路线设置:
var multiparty = require('connect-multiparty'),
multipartyMiddleware = multiparty();
app.route('/api/items/upload').post(multipartyMiddleware, items.upload);
这是 items.upload() 函数:
exports.upload = function(req, res) {
var file = req.files.file;
var s3bucket = new AWS.S3({params: {Bucket: 'mybucketname'}});
s3bucket.createBucket(function() {
var params = {
Key: file.name,
Body: file
};
s3bucket.upload(params, function(err, data) {
console.log("PRINT FILE:", file);
if (err) {
console.log('ERROR MSG: ', err);
} else {
console.log('Successfully uploaded data');
}
});
});
};
将 Body
参数设置为 "hello"
之类的字符串可以正常工作。根据 doc,Body
param 必须采用 (Buffer, Typed Array, Blob, String, ReadableStream) 对象数据。 但是,上传文件对象失败并显示以下错误消息:
[Error: Unsupported body payload object]
这是文件对象:
{ fieldName: 'file',
originalFilename: 'second_fnp.png',
path: '/var/folders/ps/l8lvygws0w93trqz7yj1t5sr0000gn/T/26374-7ttwvc.png',
headers:
{ 'content-disposition': 'form-data; name="file"; filename="second_fnp.png"',
'content-type': 'image/png' },
ws:
{ _writableState:
{ highWaterMark: 16384,
objectMode: false,
needDrain: true,
ending: true,
ended: true,
finished: true,
decodeStrings: true,
defaultEncoding: 'utf8',
length: 0,
writing: false,
sync: false,
bufferProcessing: false,
onwrite: [Function],
writecb: null,
writelen: 0,
buffer: [],
errorEmitted: false },
writable: true,
domain: null,
_events: { error: [Object], close: [Object] },
_maxListeners: 10,
path: '/var/folders/ps/l8lvygws0w93trqz7yj1t5sr0000gn/T/26374-7ttwvc.png',
fd: null,
flags: 'w',
mode: 438,
start: undefined,
pos: undefined,
bytesWritten: 261937,
closed: true },
size: 261937,
name: 'second_fnp.png',
type: 'image/png' }
任何帮助将不胜感激!
看来这里有些地方出了问题。根据您的 post,您似乎正在尝试使用 connect-multiparty
中间件支持文件上传。这个中间件所做的是获取上传的文件,将其写入本地文件系统,然后将 req.files
设置为上传的文件。
您的路由配置看起来不错,问题似乎出在您的 items.upload()
函数上。特别是这部分:
var params = {
Key: file.name,
Body: file
};
正如我在回答开头提到的 connect-multiparty
将文件写入本地文件系统,因此您需要打开文件并读取它,然后上传它,然后将其删除本地文件系统。
那是说您可以将您的方法更新为如下所示:
var fs = require('fs');
exports.upload = function (req, res) {
var file = req.files.file;
fs.readFile(file.path, function (err, data) {
if (err) throw err; // Something went wrong!
var s3bucket = new AWS.S3({params: {Bucket: 'mybucketname'}});
s3bucket.createBucket(function () {
var params = {
Key: file.originalFilename, //file.name doesn't exist as a property
Body: data
};
s3bucket.upload(params, function (err, data) {
// Whether there is an error or not, delete the temp file
fs.unlink(file.path, function (err) {
if (err) {
console.error(err);
}
console.log('Temp File Delete');
});
console.log("PRINT FILE:", file);
if (err) {
console.log('ERROR MSG: ', err);
res.status(500).send(err);
} else {
console.log('Successfully uploaded data');
res.status(200).end();
}
});
});
});
};
它所做的是从本地文件系统读取上传的文件,然后将其上传到 S3,然后删除临时文件并发送响应。
这种方法存在一些问题。首先,它的效率不如预期,因为对于大文件,您将在编写文件之前加载整个文件。其次,这个过程不支持大文件的分段上传(我认为在你必须进行分段上传之前,截止是 5 Mb)。
我建议您使用我一直在研究的模块 S3FS which provides a similar interface to the native FS in Node.JS 但抽象出一些细节,例如多部分上传和S3 api(以及添加一些附加功能,如递归方法)。
如果您要引入 S3FS 库,您的代码将如下所示:
var fs = require('fs'),
S3FS = require('s3fs'),
s3fsImpl = new S3FS('mybucketname', {
accessKeyId: XXXXXXXXXXX,
secretAccessKey: XXXXXXXXXXXXXXXXX
});
// Create our bucket if it doesn't exist
s3fsImpl.create();
exports.upload = function (req, res) {
var file = req.files.file;
var stream = fs.createReadStream(file.path);
return s3fsImpl.writeFile(file.originalFilename, stream).then(function () {
fs.unlink(file.path, function (err) {
if (err) {
console.error(err);
}
});
res.status(200).end();
});
};
这将执行的操作是为提供的存储桶和 AWS 凭据实例化模块,然后创建存储桶(如果不存在)。然后,当上传文件的请求通过时,我们将打开文件流,并使用它将文件写入 S3 的指定路径。这将在幕后处理多部分上传片段(如果需要)并且具有通过流完成的好处,因此您不必在开始上传之前等待读取整个文件。
如果您愿意,可以将代码更改为使用事件侦听器从 Promises. Or use the pipe() 方法回调以确定 end/errors。
如果您正在寻找其他方法,请查看 s3fs 的文档,如果您正在寻找其他方法或遇到问题,请随时提出问题。
我发现以下是可行的解决方案::
npm install aws-sdk
安装 aws-sdk 后,使用以下代码在需要的地方替换值。
var AWS = require('aws-sdk');
var fs = require('fs');
var s3 = new AWS.S3();
// Bucket names must be unique across all S3 users
var myBucket = 'njera';
var myKey = 'jpeg';
//for text file
//fs.readFile('demo.txt', function (err, data) {
//for Video file
//fs.readFile('demo.avi', function (err, data) {
//for image file
fs.readFile('demo.jpg', function (err, data) {
if (err) { throw err; }
params = {Bucket: myBucket, Key: myKey, Body: data };
s3.putObject(params, function(err, data) {
if (err) {
console.log(err)
} else {
console.log("Successfully uploaded data to myBucket/myKey");
}
});
});
我在这里找到了有关该主题的完整教程,以供您查找参考 ::
How to upload files (text/image/video) in amazon s3 using node.js
或使用承诺:
const AWS = require('aws-sdk');
AWS.config.update({
accessKeyId: 'accessKeyId',
secretAccessKey: 'secretAccessKey',
region: 'region'
});
let params = {
Bucket: "yourBucketName",
Key: 'someUniqueKey',
Body: 'someFile'
};
try {
let uploadPromise = await new AWS.S3().putObject(params).promise();
console.log("Successfully uploaded data to bucket");
} catch (e) {
console.log("Error uploading data: ", e);
}
var express = require('express')
app = module.exports = express();
var secureServer = require('http').createServer(app);
secureServer.listen(3001);
var aws = require('aws-sdk')
var multer = require('multer')
var multerS3 = require('multer-s3')
aws.config.update({
secretAccessKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
accessKeyId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
region: 'us-east-1'
});
s3 = new aws.S3();
var upload = multer({
storage: multerS3({
s3: s3,
dirname: "uploads",
bucket: "Your bucket name",
key: function (req, file, cb) {
console.log(file);
cb(null, "uploads/profile_images/u_" + Date.now() + ".jpg"); //use
Date.now() for unique file keys
}
})
});
app.post('/upload', upload.single('photos'), function(req, res, next) {
console.log('Successfully uploaded ', req.file)
res.send('Successfully uploaded ' + req.file.length + ' files!')
})
正在将文件上传到 AWS s3 并发送 url 作为访问文件的响应。
Multer 是一个 node.js 中间件,用于处理 multipart/form-data,主要用于上传文件。它写在 busboy 之上以实现最高效率。检查这个 npm 模块 here.
发送请求时,请确保 headers,Content-Type 为 multipart/form-data。 我们在响应中发送文件位置,这将给出 url,但如果你想访问那个 url,请创建存储桶 public 否则你将无法访问它。
upload.router.js
const express = require('express');
const router = express.Router();
const AWS = require('aws-sdk');
const multer = require('multer');
const storage = multer.memoryStorage()
const upload = multer({storage: storage});
const s3Client = new AWS.S3({
accessKeyId: 'your_access_key_id',
secretAccessKey: 'your_secret_access_id',
region :'ur region'
});
const uploadParams = {
Bucket: 'ur_bucket_name',
Key: '', // pass key
Body: null, // pass file body
};
router.post('/api/file/upload', upload.single("file"),(req,res) => {
const params = uploadParams;
uploadParams.Key = req.file.originalname;
uploadParams.Body = req.file.buffer;
s3Client.upload(params, (err, data) => {
if (err) {
res.status(500).json({error:"Error -> " + err});
}
res.json({message: 'File uploaded successfully','filename':
req.file.originalname, 'location': data.Location});
});
});
module.exports = router;
app.js
const express = require('express');
const app = express();
const router = require('./app/routers/upload.router.js');
app.use('/', router);
// Create a Server
const server = app.listen(8080, () => {
console.log("App listening at 8080");
})
感谢 David,他的解决方案帮助我想出了将多部分文件从我的 Heroku 托管站点上传到 S3 存储桶的解决方案。我使用 formidable 来处理传入的表单和 fs 来获取文件内容。希望对您有所帮助。
api.service.ts
public upload(files): Observable<any> {
const formData: FormData = new FormData();
files.forEach(file => {
// create a new multipart-form for every file
formData.append('file', file, file.name);
});
return this.http.post(uploadUrl, formData).pipe(
map(this.extractData),
catchError(this.handleError));
}
}
server.js
app.post('/api/upload', upload);
app.use('/api/upload', router);
upload.js
const IncomingForm = require('formidable').IncomingForm;
const fs = require('fs');
const AWS = require('aws-sdk');
module.exports = function upload(req, res) {
var form = new IncomingForm();
const bucket = new AWS.S3(
{
signatureVersion: 'v4',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: 'us-east-1'
}
);
form.on('file', (field, file) => {
const fileContent = fs.readFileSync(file.path);
const s3Params = {
Bucket: process.env.AWS_S3_BUCKET,
Key: 'folder/' + file.name,
Expires: 60,
Body: fileContent,
ACL: 'public-read'
};
bucket.upload(s3Params, function(err, data) {
if (err) {
throw err;
}
console.log('File uploaded to: ' + data.Location);
fs.unlink(file.path, function (err) {
if (err) {
console.error(err);
}
console.log('Temp File Delete');
});
});
});
// The second callback is called when the form is completely parsed.
// In this case, we want to send back a success status code.
form.on('end', () => {
res.status(200).json('upload ok');
});
form.parse(req);
}
上传-image.component.ts
import { Component, OnInit, ViewChild, Output, EventEmitter, Input } from '@angular/core';
import { ApiService } from '../api.service';
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({
selector: 'app-upload-image',
templateUrl: './upload-image.component.html',
styleUrls: ['./upload-image.component.css']
})
export class UploadImageComponent implements OnInit {
public files: Set<File> = new Set();
@ViewChild('file', { static: false }) file;
public uploadedFiles: Array<string> = new Array<string>();
public uploadedFileNames: Array<string> = new Array<string>();
@Output() filesOutput = new EventEmitter<Array<string>>();
@Input() CurrentImage: string;
@Input() IsPublic: boolean;
@Output() valueUpdate = new EventEmitter();
strUploadedFiles:string = '';
filesUploaded: boolean = false;
constructor(private api: ApiService, public snackBar: MatSnackBar,) { }
ngOnInit() {
}
updateValue(val) {
this.valueUpdate.emit(val);
}
reset()
{
this.files = new Set();
this.uploadedFiles = new Array<string>();
this.uploadedFileNames = new Array<string>();
this.filesUploaded = false;
}
upload() {
this.api.upload(this.files).subscribe(res => {
this.filesOutput.emit(this.uploadedFiles);
if (res == 'upload ok')
{
this.reset();
}
}, err => {
console.log(err);
});
}
onFilesAdded() {
var txt = '';
const files: { [key: string]: File } = this.file.nativeElement.files;
for (let key in files) {
if (!isNaN(parseInt(key))) {
var currentFile = files[key];
var sFileExtension = currentFile.name.split('.')[currentFile.name.split('.').length - 1].toLowerCase();
var iFileSize = currentFile.size;
if (!(sFileExtension === "jpg"
|| sFileExtension === "png")
|| iFileSize > 671329) {
txt = "File type : " + sFileExtension + "\n\n";
txt += "Size: " + iFileSize + "\n\n";
txt += "Please make sure your file is in jpg or png format and less than 655 KB.\n\n";
alert(txt);
return false;
}
this.files.add(files[key]);
this.uploadedFiles.push('https://gourmet-philatelist-assets.s3.amazonaws.com/folder/' + files[key].name);
this.uploadedFileNames.push(files[key].name);
if (this.IsPublic && this.uploadedFileNames.length == 1)
{
this.filesUploaded = true;
this.updateValue(files[key].name);
break;
}
else if (!this.IsPublic && this.uploadedFileNames.length == 3)
{
this.strUploadedFiles += files[key].name;
this.updateValue(this.strUploadedFiles);
this.filesUploaded = true;
break;
}
else
{
this.strUploadedFiles += files[key].name + ",";
this.updateValue(this.strUploadedFiles);
}
}
}
}
addFiles() {
this.file.nativeElement.click();
}
openSnackBar(message: string, action: string) {
this.snackBar.open(message, action, {
duration: 2000,
verticalPosition: 'top'
});
}
}
上传-image.component.html
<input type="file" #file style="display: none" (change)="onFilesAdded()" multiple />
<button mat-raised-button color="primary"
[disabled]="filesUploaded" (click)="$event.preventDefault(); addFiles()">
Add Files
</button>
<button class="btn btn-success" [disabled]="uploadedFileNames.length == 0" (click)="$event.preventDefault(); upload()">
Upload
</button>
上传CSV/Excel
const fs = require('fs');
const AWS = require('aws-sdk');
const s3 = new AWS.S3({
accessKeyId: XXXXXXXXX,
secretAccessKey: XXXXXXXXX
});
const absoluteFilePath = "C:\Project\test.xlsx";
const uploadFile = () => {
fs.readFile(absoluteFilePath, (err, data) => {
if (err) throw err;
const params = {
Bucket: 'testBucket', // pass your bucket name
Key: 'folderName/key.xlsx', // file will be saved in <folderName> folder
Body: data
};
s3.upload(params, function (s3Err, data) {
if (s3Err) throw s3Err
console.log(`File uploaded successfully at ${data.Location}`);
debugger;
});
});
};
uploadFile();
适合我:)
const fileContent = fs.createReadStream(`${fileName}`);
return new Promise(function (resolve, reject) {
fileContent.once('error', reject);
s3.upload(
{
Bucket: 'test-bucket',
Key: `${fileName + '_' + Date.now().toString()}`,
ContentType: 'application/pdf',
ACL: 'public-read',
Body: fileContent
},
function (err, result) {
if (err) {
reject(err);
return;
}
resolve(result.Location);
}
);
});```
使用 aws SDK v3
npm install @aws-sdk/client-s3
上传代码
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
/**
* advisable to save your AWS credentials and configurations in an environmet file. Not inside the code
* AWS lib will automatically load the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY if available in your environment
*/
const s3Client = new S3Client({ region: process.env.AWS_S3_REGION });
/**
* upload a file
* @param file the file object to be uploaded
* @param fileKey the fileKey. could be separated with '/' to nest the file into a folder structure. eg. members/user1/profile.png
*/
export function uploadFile(file, fileKey){
s3Client.send(new PutObjectCommand({
Bucket: process.env.MY_AWS_S3_BUCKET,
Key: fileKey,
Body: file
}));
}
如果你想下载
import { GetObjectCommand } from "@aws-sdk/client-s3";
/**
* download a file from AWS and send to your rest client
*/
app.get('/download', function(req, res, next){
var fileKey = req.query['fileKey'];
var bucketParams = {
Bucket: 'my-bucket-name',
Key: fileKey,
};
res.attachment(fileKey);
var fileStream = await s3Client.send(new GetObjectCommand(bucketParams));
// for TS you can add: if (fileStream.Body instanceof Readable)
fileStream.Body.pipe(res)
});