使用 Angular ng-file-upload 上传 Scala Play Framework 图片
Scala Play Framework image upload with Angular ng-file-upload
我在前端使用 Angular ng-file-upload
(https://github.com/danialfarid/ng-file-upload) 来管理文件上传过程。
很遗憾,表单包含一个包含多个文件的复杂对象。在服务器端使用 MultipartFormData
(https://www.playframework.com/documentation/2.5.x/ScalaBodyParsers) 我已经成功地分解了上传的内容并且可以从 request.body
.
读取它
现在,令我惊讶的是,我没有一个简单的 Json Objects
,而是一个奇怪的数据类型,在 ng-file-upload
网站上描述为:
(...) server implementations expecting nested data object keys in .key or [key] format.
Example: data: {rec: {name: 'N', pic: file}} sent as: rec[name] -> N, rec[pic] -> file
data: {rec: {name: 'N', pic: file}, objectKey: '.k'} sent as: rec.name -> N, rec.pic -> file
到目前为止,我已经设法将所有数据转换为通用的 MultipartFormData.Part
类型,使用 DataPart
和 FilePart
如下所示:
val opts = body.dataParts.map {
case (key, values) => DataPart(key, values.head)
}
val parts = opts ++ body.files
所以我现在很不幸Iterable[Part]
:
0 = {MultipartFormData$DataPart@86271} "DataPart(arabic[active],false)"
1 = {MultipartFormData$DataPart@86273} "DataPart(english[active],true)"
2 = {MultipartFormData$DataPart@86277} "DataPart(english[url],2132132132)"
...
7 = {MultipartFormData$FilePart@76473} "FilePart(english[image],fb_icon_325x325.png,Some(image/png),TemporaryFile(/tmp/playtemp5909927824995768544/multipartBody8348573128070542611asTemporaryFile))"
每个对象 name
包含其 Json 结构的 key
及其相应的 value
。现在我想将它解析为对象,而不是 key[level1][level2]
,在我的例子中:
case class PcBanner(english: PcBanners, arabic: PcBanners, kurdish: PcBanners)
case class PcBanners(active: Boolean, url: Option[String], image: Option[String])`
我希望你明白了。
问题
我知道我可以尝试解析 name
字符串以使其适合对象,但我相信我在中间的某个地方犯了一个错误。
有没有办法将这个结构解析为对象,使用字段名称作为参考?有内置的 Play 功能或类似功能吗?
感谢您的帮助!
正如我在标题中所述,我的案例是发送 图片 。如您所料,我还展示了预览和当前保存在数据库中的文件。
考虑到所有优点和缺点,我决定以 JSON 格式发送所有数据,两种方式。这意味着图像在 JSON 结构中被编码和发送。
尽管上面的解决方案看起来很方便,但实际上在实施过程中会产生新的问题。
- 您将很快超过服务器的 POST 请求大小限制。对于 Play 服务器,可以扩展默认的 100kB,但是...
- 我很快 运行 发现了一些数据畸形,因为保存为巨大字节字符串的图像可能有一些 sending/parsing 错误。
我没有深入研究这个错误的解决方案,我使用了@danial 的建议:
No have the file sent separately like this
{file: file, otherData: JSON.stringify(myData)}
我的解决方案
如果有人想对我使用类似的方法,我会提出我的答案。
在 front-end 方面,我决定使用 ng-file-upload 库。使用 ngf-select
和 ngf-drop
将其绑定到 HTML 组件,从而启用组件:
<div ngf-drop ngf-select
ng-model="image"
ngf-accept="'image/*'"
ngf-resize="{width: {{width}}, height: {{height}}, quality: 1.0, restoreExif: false}">
<img ng-show="!!image && !!image.$ngfName" ngf-src="image">
<img ng-show="(!image || !image.$ngfName)" ng-src="{{ imageUrl }}">
</div>
在上传标签内我放了图片预览。这完美无缺。如果未选择图像,我将使用保存在数据库中的图像。
数据和图像不再共享模型。上传功能如下所示:
return Upload.upload({
url: url,
data: {file: images, data: angular.toJson(data)}
}).then(function (resp) {
console.log(resp);
}, function (error) {
console.log(error);
});
将以上所有内容放在一起得到了输出数据 object:
{
"english":{
"active":true,
"url":"http://google.com"
},
"arabic":{
"active":true,
"url":"http://google.com"
},
"kurdish":{
"active":true,
"url":"http://google.com"
}
}
在服务器端,JSON 匹配准备好的案例 class 并使用 build-in Jackson 解析器进行解析,允许轻松 object 操作。图片必须手动选择:
val json = r.body.dataParts("data")
val jsValue = Json.parse(json.head)
val result = jsValue.validate(LocalizedBanner.dataModelFormat) // parse JSON
从 body 中提取文件可以通过内置函数 .file
:
完成
val key = s"file[${lang.name}][${imageType.name}]"
body.file(key).map(mp => (mp.ref.file, imageType))
尽情享受吧!
我在前端使用 Angular ng-file-upload
(https://github.com/danialfarid/ng-file-upload) 来管理文件上传过程。
很遗憾,表单包含一个包含多个文件的复杂对象。在服务器端使用 MultipartFormData
(https://www.playframework.com/documentation/2.5.x/ScalaBodyParsers) 我已经成功地分解了上传的内容并且可以从 request.body
.
现在,令我惊讶的是,我没有一个简单的 Json Objects
,而是一个奇怪的数据类型,在 ng-file-upload
网站上描述为:
(...) server implementations expecting nested data object keys in .key or [key] format. Example: data: {rec: {name: 'N', pic: file}} sent as: rec[name] -> N, rec[pic] -> file
data: {rec: {name: 'N', pic: file}, objectKey: '.k'} sent as: rec.name -> N, rec.pic -> file
到目前为止,我已经设法将所有数据转换为通用的 MultipartFormData.Part
类型,使用 DataPart
和 FilePart
如下所示:
val opts = body.dataParts.map {
case (key, values) => DataPart(key, values.head)
}
val parts = opts ++ body.files
所以我现在很不幸Iterable[Part]
:
0 = {MultipartFormData$DataPart@86271} "DataPart(arabic[active],false)"
1 = {MultipartFormData$DataPart@86273} "DataPart(english[active],true)"
2 = {MultipartFormData$DataPart@86277} "DataPart(english[url],2132132132)"
...
7 = {MultipartFormData$FilePart@76473} "FilePart(english[image],fb_icon_325x325.png,Some(image/png),TemporaryFile(/tmp/playtemp5909927824995768544/multipartBody8348573128070542611asTemporaryFile))"
每个对象 name
包含其 Json 结构的 key
及其相应的 value
。现在我想将它解析为对象,而不是 key[level1][level2]
,在我的例子中:
case class PcBanner(english: PcBanners, arabic: PcBanners, kurdish: PcBanners)
case class PcBanners(active: Boolean, url: Option[String], image: Option[String])`
我希望你明白了。
问题
我知道我可以尝试解析 name
字符串以使其适合对象,但我相信我在中间的某个地方犯了一个错误。
有没有办法将这个结构解析为对象,使用字段名称作为参考?有内置的 Play 功能或类似功能吗?
感谢您的帮助!
正如我在标题中所述,我的案例是发送 图片 。如您所料,我还展示了预览和当前保存在数据库中的文件。
考虑到所有优点和缺点,我决定以 JSON 格式发送所有数据,两种方式。这意味着图像在 JSON 结构中被编码和发送。
尽管上面的解决方案看起来很方便,但实际上在实施过程中会产生新的问题。
- 您将很快超过服务器的 POST 请求大小限制。对于 Play 服务器,可以扩展默认的 100kB,但是...
- 我很快 运行 发现了一些数据畸形,因为保存为巨大字节字符串的图像可能有一些 sending/parsing 错误。
我没有深入研究这个错误的解决方案,我使用了@danial 的建议:
No have the file sent separately like this
{file: file, otherData: JSON.stringify(myData)}
我的解决方案
如果有人想对我使用类似的方法,我会提出我的答案。
在 front-end 方面,我决定使用 ng-file-upload 库。使用 ngf-select
和 ngf-drop
将其绑定到 HTML 组件,从而启用组件:
<div ngf-drop ngf-select
ng-model="image"
ngf-accept="'image/*'"
ngf-resize="{width: {{width}}, height: {{height}}, quality: 1.0, restoreExif: false}">
<img ng-show="!!image && !!image.$ngfName" ngf-src="image">
<img ng-show="(!image || !image.$ngfName)" ng-src="{{ imageUrl }}">
</div>
在上传标签内我放了图片预览。这完美无缺。如果未选择图像,我将使用保存在数据库中的图像。
数据和图像不再共享模型。上传功能如下所示:
return Upload.upload({
url: url,
data: {file: images, data: angular.toJson(data)}
}).then(function (resp) {
console.log(resp);
}, function (error) {
console.log(error);
});
将以上所有内容放在一起得到了输出数据 object:
{
"english":{
"active":true,
"url":"http://google.com"
},
"arabic":{
"active":true,
"url":"http://google.com"
},
"kurdish":{
"active":true,
"url":"http://google.com"
}
}
在服务器端,JSON 匹配准备好的案例 class 并使用 build-in Jackson 解析器进行解析,允许轻松 object 操作。图片必须手动选择:
val json = r.body.dataParts("data")
val jsValue = Json.parse(json.head)
val result = jsValue.validate(LocalizedBanner.dataModelFormat) // parse JSON
从 body 中提取文件可以通过内置函数 .file
:
val key = s"file[${lang.name}][${imageType.name}]"
body.file(key).map(mp => (mp.ref.file, imageType))
尽情享受吧!