我想做什么: 有一个html表单,里面有文件输入。选择文件时,文件输入应上传文件,并获取文件ID,因此在提交表单时,文件ID将与表单一起发布并写入数据库。
更短的版本:我想用我的文件存储元数据(例如id)。
听起来很简单,但我很难在LoopBack中做到这一点。
关于这个主题已经进行了几次对话(1,2),似乎都没有找到解决方案,所以我认为这可能是找到一次和所有
最简单的解决方案是使用模型关系,但LoopBack不支持与文件存储服务的关系。凹凸。所以我们必须使用名为File
的persistedmodel,并覆盖默认的create,delete,以便保存和删除我所拥有的文件存储模型 - 名为Storage
。
到目前为止我的设置:
name
,size
,url
和objectId
create
之前设置了一个远程钩子,因此可以先保存文件,然后将url
注入File.create()
我在那里,根据this LoopBack page,我有ctx,里面应该有文件:
File.beforeRemote('create', function(ctx, affectedModelInstance, next) {})`
什么是ctx
?
ctx.req
:快速请求对象ctx.result
:快速响应对象。
好的,所以现在我在Express页面上,非常迷失,这就是一个关于“身体解析中间件”的东西,我不知道它可能是什么。
我觉得我接近解决方案,任何帮助都会受到赞赏。这种方法对吗?
答案 0 :(得分:56)
这里是完整的解决方案,用于存储带有环回文件的元数据。
您需要一个容器模型
ALTER proc [dbo].[spMySP]
@Object nvarchar(20), -- comments
@Type nchar(1), -- comments
@num_of_cols_in_key int,
@listCols nvarchar(255),
@ListColdel nvarchar(255)
AS
begin
-- Return values
common/models/container.json
在{
"name": "container",
"base": "Model",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {},
"acls": [],
"methods": []
}
中为容器创建数据源。例如:
server/datasources.json
您需要在...
"storage": {
"name": "storage",
"connector": "loopback-component-storage",
"provider": "filesystem",
"root": "/var/www/storage",
"maxFileSize": "52428800"
}
...
中将此模型的数据源设置为您拥有的server/model-config.json
:
loopback-component-storage
您还需要一个文件模型来存储元数据并处理容器调用:
...
"container": {
"dataSource": "storage",
"public": true
}
...
common/models/files.json
现在将{
"name": "files",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string"
},
"type": {
"type": "string"
},
"url": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {},
"acls": [],
"methods": []
}
与files
:
container
common/models/files.js
为了公开文件api,将var CONTAINERS_URL = '/api/containers/';
module.exports = function(Files) {
Files.upload = function (ctx,options,cb) {
if(!options) options = {};
ctx.req.params.container = 'common';
Files.app.models.container.upload(ctx.req,ctx.result,options,function (err,fileObj) {
if(err) {
cb(err);
} else {
var fileInfo = fileObj.files.file[0];
Files.create({
name: fileInfo.name,
type: fileInfo.type,
container: fileInfo.container,
url: CONTAINERS_URL+fileInfo.container+'/download/'+fileInfo.name
},function (err,obj) {
if (err !== null) {
cb(err);
} else {
cb(null, obj);
}
});
}
});
};
Files.remoteMethod(
'upload',
{
description: 'Uploads a file',
accepts: [
{ arg: 'ctx', type: 'object', http: { source:'context' } },
{ arg: 'options', type: 'object', http:{ source: 'query'} }
],
returns: {
arg: 'fileObject', type: 'object', root: true
},
http: {verb: 'post'}
}
);
};
文件添加到model-config.json
模型,请记住选择正确的数据源:
files
完成!现在,您可以在...
"files": {
"dataSource": "db",
"public": true
}
...
表单字段中使用文件二进制数据调用POST /api/files/upload
。您将获得ID,姓名,类型和网址作为回报。
答案 1 :(得分:10)
我遇到了同样的问题。我通过创建自己的模型来存储元数据和我自己的上传方法来解决它。
我创建了一个模型File
,它将存储名称,类型,网址,userId(与您的相同)等信息
我创建了自己的上传远程方法,因为我无法使用钩子。容器模型是由loopback-component-storage创建的模型。
var fileInfo = fileObj.files.myFile[0];
此处myFile是文件上传的字段名称,因此您必须相应地更改它。如果您没有指定任何字段,那么它将显示为fileObj.file.null[0]
。
此代码缺少正确的错误检查,请在将其部署到生产中之前执行。
File.uploadFile = function (ctx,options,cb) {
File.app.models.container.upload(ctx.req,ctx.result,options,function (err,fileObj) {
if(err) cb(err);
else{
// Here myFile is the field name associated with upload. You should change it to something else if you
var fileInfo = fileObj.files.myFile[0];
File.create({
name: fileInfo.name,
type: fileInfo.type,
container: fileInfo.container,
userId: ctx.req.accessToken.userId,
url: CONTAINERS_URL+fileInfo.container+'/download/'+fileInfo.name // This is a hack for creating links
},function (err,obj) {
if(err){
console.log('Error in uploading' + err);
cb(err);
}
else{
cb(null,obj);
}
});
}
});
};
File.remoteMethod(
'uploadFile',
{
description: 'Uploads a file',
accepts: [
{ arg: 'ctx', type: 'object', http: { source:'context' } },
{ arg: 'options', type 'object', http:{ source: 'query'} }
],
returns: {
arg: 'fileObject', type: 'object', root: true
},
http: {verb: 'post'}
}
);
答案 2 :(得分:8)
对于那些正在寻找问题答案的人"如何在上传文件之前检查文件格式" 。
在这种情况下,我们可以使用可选参数 allowedContentTypes 。
在目录 boot 中使用示例代码:
module.exports = function(server) {
server.dataSources.filestorage.connector.allowedContentTypes = ["image/jpg", "image/jpeg", "image/png"];
}
我希望它会帮助别人。
答案 3 :(得分:1)
根据您的情况,可能值得考虑使用签名或类似功能,允许直接上传到Amazon S3,TransloadIT(用于图像处理)或类似服务。
我们对这个概念的第一个决定是,当我们使用GraphQL时,我们希望通过GraphQL避免多部分表单上传,而GraphQL又需要转移到我们后面的Loopback服务。此外,我们希望保持这些服务器的高效率,而不会通过(大)上传和相关文件验证和处理来占用资源。
您的工作流程可能如下所示:
对于横幅或头像上传等操作的情况,步骤1已经存在,因此我们跳过了这一步。
此外,您可以将SNS或SQS通知添加到S3存储桶,以在数据库中确认相关对象现在已附加文件 - 实际上是步骤4。
这是一个多步骤的过程,但可以很好地消除在核心API中处理文件上传的需要。到目前为止,这对于用户头像和将PDF附加到记录等内容的初始实现(此项目的早期阶段)运行良好。
示例参考:
http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingHTTPPOST.html
答案 4 :(得分:1)
对于其他任何遇到 loopback 3和Postman 问题的人,在POST时,连接会挂起(或返回ERR_EMPTY_RESPONSE)(在此处的某些评论中看到)...此方案中的问题是, Postman使用的内容类型为“application / x-www-form-urlencoded”!
请删除该标题并添加“Accept”=“multipart / form-data”。我已经在环回中为此行为提交了一个错误
答案 5 :(得分:0)
只需将数据作为"params"
对象传递,然后在服务器上将其设为ctx.req.query
例如
在客户端
Upload.upload(
{
url: '/api/containers/container_name/upload',
file: file,
//Additional data with file
params:{
orderId: 1,
customerId: 1,
otherImageInfo:[]
}
});
在服务器端
假设您的存储模型名称为container
Container.beforeRemote('upload', function(ctx, modelInstance, next) {
//OUPTUTS: {orderId:1, customerId:1, otherImageInfo:[]}
console.log(ctx.req.query);
next();
})
答案 6 :(得分:0)
对于AngularJS SDK用户...如果您想使用像Container.upload()这样的生成方法,您可能需要添加一行来配置lb-services.js
中的方法以将Content-Type标头设置为undefined
。这将允许客户端设置Content-Type标头并自动添加边界值。看起来像这样:
"upload": {
url: urlBase + "/containers/:container/upload",
method: "POST",
headers: {"Content-Type": undefined}
}