简体   繁体   English

使用Angular ng-file-upload上传Scala Play Framework图像

[英]Scala Play Framework image upload with Angular ng-file-upload

I am using Angular ng-file-upload ( https://github.com/danialfarid/ng-file-upload ) on the frontend to manage the file upload process. 我在前端使用Angular ng-file-uploadhttps://github.com/danialfarid/ng-file-upload )管理文件上传过程。

Unfortunately, form contains a complex object with multiple files. 不幸的是,表单包含具有多个文件的复杂对象。 Using the MultipartFormData ( https://www.playframework.com/documentation/2.5.x/ScalaBodyParsers ) on the server side I have successfully decomposed the uploaded content and can read it from the request.body . 使用服务器端的MultipartFormDatahttps://www.playframework.com/documentation/2.5.x/ScalaBodyParsers ),我已经成功分解了上传的内容,并且可以从request.body读取它。

Now, to my surprise, I do not have a simple Json Objects but rather a strangely formed datatype, described on the ng-file-upload website as: 现在,令我惊讶的是,我没有一个简单的Json Objects ,而是一个奇怪地形成的数据类型,在ng-file-upload网站上描述为:

(...) server implementations expecting nested data object keys in .key or [key] format. (...)服务器实现期望使用.key或[key]格式的嵌套数据对象键。 Example: data: {rec: {name: 'N', pic: file}} sent as: rec[name] -> N, rec[pic] -> file 示例:数据:{记录:{名称:'N',图片:文件}}发送为:记录[名称]-> N,记录[图片]->文件
data: {rec: {name: 'N', pic: file}, objectKey: '.k'} sent as: rec.name -> N, rec.pic -> file 数据:{记录:{名称:'N',图片:文件},对象关键字:'。k'}发送为:记录名称-> N,记录图片->文件

So far I have managed to bring all the data to a common MultipartFormData.Part type, using the DataPart and FilePart like this: 到目前为止,我已经设法使用DataPartFilePart将所有数据转换为通用的MultipartFormData.Part类型:

 val opts = body.dataParts.map {
   case (key, values) => DataPart(key, values.head)
 }

 val parts = opts ++ body.files

So I am now left with a quite unfortunate Iterable[Part] : 因此,我现在有一个非常不幸的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))"

Each object name contains the key of it's Json structure and its according value . 每个对象name包含其Json结构的key及其相应的value Now instead of key[level1][level2] I would like to parse it to objects, in my case: 现在,我想将其解析为对象而不是key[level1][level2]

case class PcBanner(english: PcBanners, arabic: PcBanners, kurdish: PcBanners)
case class PcBanners(active: Boolean, url: Option[String], image: Option[String])`

I hope you got the idea. 我希望你有主意。

The question 问题

I know I could try to parse the name strings trying to fit it to objects, but I believe I made a mistake someway in the middle. 我知道我可以尝试解析name字符串以使其适合对象,但是我相信中间还是有一个错误。 Is there a way to parse this structure into the objects, using field names as a reference? 有没有一种方法可以使用字段名称作为参考来将此结构解析为对象? Any build in Play functions or alike? 播放功能有内置功能吗?

Thanks for help! 感谢帮助!

As I stated in the title my case was to send images . 正如我在标题中所述,我的情况是发送图像 As you would expect, I am also presenting a preview and the files currently saved in the database. 如您所料,我还将展示一个预览以及当前保存在数据库中的文件。

Considering all pros and cons I have decided to send all the data in JSON format, both ways. 考虑到所有优点和缺点,我决定以两种方式均以JSON格式发送所有数据。 Meaning that the images are encoded and sent along in JSON structure. 这意味着图像将以JSON结构进行编码和发送。

Despite the fact that above solution looks very convenient it actually creates new problems during the implementation. 尽管上述解决方案看起来非常方便,但实际上在实施过程中会产生新的问题。

  1. You will quickly exceed the server's POST request size limit. 您将很快超过服务器的POST请求大小限制。 For Play server the default 100kB is possible to be extended, but... 对于Play服务器,可以扩展默认100kB,但是...
  2. I have soon run into some data malformations as the image saved as huge String of bytes probably had some sending/parsing errors. 我很快遇到了一些数据格式错误,因为保存为巨大字节字符串的图像可能存在一些发送/解析错误。

Not going deeper into this faulty solution I have used the @danial advice: 对于这个错误的解决方案,我不会使用更深的含义,而是使用@danial建议:

No have the file sent separately like this 没有像这样单独发送文件
{file: file, otherData: JSON.stringify(myData)}

My solution 我的解决方案

If anyone would like to use similar approach to mine I present my answer. 如果有人想使用类似的方法来挖掘我的答案。 On the front-end side I have decided used ng-file-upload library. 在前端,我决定使用ng-file-upload库。 Binding it to HTML component with ngf-select with ngf-drop which enables the component: 它具有结合到HTML组件ngf-selectngf-drop这使得部件:

<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>

Inside the upload tag I put the image preview. 在上载标签中,我放置了图像预览。 This works flawlessly. 这可以完美地工作。 If the image is not selected I use the image saved in the db. 如果未选择图像,则使用保存在数据库中的图像。

The data and images do not share the model anymore. 数据和图像不再共享模型。 The upload function looks as follow: 上传功能如下:

return Upload.upload({
    url: url,
    data: {file: images, data: angular.toJson(data)}
}).then(function (resp) {
    console.log(resp);
}, function (error) {
    console.log(error);
});

Putting together all the above gave me the output data object: 将以上所有内容放在一起,便得到了输出数据对象:

{  
   "english":{  
      "active":true,
      "url":"http://google.com"
   },
   "arabic":{  
      "active":true,
      "url":"http://google.com"
   },
   "kurdish":{  
      "active":true,
      "url":"http://google.com"
   }
}

On the server side the JSON matches the prepared case class and is parsed with build-in Jackson parser, allowing for easy object manipulation. 在服务器端,JSON与准备好的case类匹配,并使用内置的Jackson解析器进行解析,从而简化了对象操作。 The image has to be manually selected: 该图像必须手动选择:

val json = r.body.dataParts("data")
val jsValue = Json.parse(json.head)
val result = jsValue.validate(LocalizedBanner.dataModelFormat) // parse JSON

Extracting the files from body can be done with build in function .file : 可以使用内置函数.file从主体中提取文件:

val key = s"file[${lang.name}][${imageType.name}]"
body.file(key).map(mp => (mp.ref.file, imageType))

Enjoy! 请享用!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM