3

Scala/Play 大师在那里。

我正在尝试在 Play 2.1 (RC1) 中使用 AJAX 上传文件。对于客户端部分,我使用的是eldarion/bootstrap-ajax,一切似乎都很好,除了上传的文件是空的。

前端片段:

...
<form action="@routes.Campaigns.upload" method="post" class="form ajax replaceable" data-replace=".replaceable">
    <input type="file" name="picture">
    <p><input class="btn" type="submit"></p>
</form>
...

请注意,我必须使用显式<form>标记而不是@form帮助程序,因为所需的 css 类(数据替换)包含破折号,因此不能用作Symbol. 但不管怎么说。控制器中调用的动作如下所示:

  def upload = Action(parse.temporaryFile) {
    request =>
      Logger.info("Trying to upload a file")
      val resultString = try {
        val file = new File("/tmp/picture")
        request.body.moveTo(file, true)
        "file has been uploaded"
      } catch {
        case e: Exception => "an error has occurred while uploading the file"
      }
      val jsonResponse = Json.toJson(
        Map("html" -> Json.toJson("<p>" + resultString + "</p>")
        )
      )
      Ok(jsonResponse)
  }

我知道随着我的发展,文件名应该更智能地设置,但目前,/tmp/picture 对我来说和其他任何名称一样好。

生成 JSON 响应(其中包含“文件已上传”消息),并作为 200 响应的有效负载发送回浏览器。JSON 被接收并正确地用于修改页面(在这种情况下,只是删除了上传的表单)。

但是该文件虽然出现在正确的时间和正确的位置,但始终是空的:

larsson:tmp bruno$ ls -l /tmp/picture
-rw-r--r--  1 bruno  staff  0  7 Jan 03:07 /tmp/picture

在我看来,这特别奇怪,因为使用传统multipart/form-data形式的上传代码,没有任何 AJAX,并且使用Actionwithparse.multipartFormData作为参数,而不是parse.temporaryFile,可以正常工作。

任何帮助将不胜感激。提前致谢。

4

2 回答 2

3

我不知道 bootstrap-ajax,无论如何,如果它没有专门支持通过 AJAX 上传文件(而且我在它的readme文件中没有找到任何关于这种可能性的信息)它不会使用 AJAX 发送文件。

原因:在标准 JavaScript 中,由于安全限制,无法使用 AJAX 上传文件,iFrames并且有一些技术可以解决此问题,主要是bootstrap-ajax使用解决方案。

解决方案:有一些 AJAX 文件上传器,它适用于 HTML5 即。jQuery File Upload提供 ajax 上传、多文件上传、拖拽文件到拖放区等。

一般来说,HTML5 比早期版本的 HTML 更好地支持文件上传,因此您可以轻松构建上传器而无需使用额外的插件,看看这个主题。正如您所看到的,它提供了在上传之前验证某些数据的可能性,并且还提供了进度条。

于 2013-01-07T05:03:10.417 回答
1

我目前正在尝试实现类似的东西,并且我的第一个版本正在运行。我就是这样做的:

在我的控制器中,我定义了一种上传文件的方法。就我而言,我使用 Action.async ,因为我使用reactivemongo将内容保存到我的 MongoDB 中。我已删除该代码,以免使此示例复杂化。

我在这个示例中所做的是上传一个 csv 文件,将其保存到磁盘,然后将第一行作为字符串返回给用户。在现实生活中,该方法会生成一个列表,以便用户能够选择哪一列代表什么等等。

我使用强大的 csv进行 csv 解析。伟大的图书馆!

应用:

def upload = Action.async(parse.multipartFormData) {
    implicit request =>
      val result = uploadForm.bindFromRequest().fold(
        errorForm => Future(BadRequest(views.html.index(errorForm))),
        form => {
          import java.io.File
          request.body.file("csvFile").map {
            csv =>
              val path = current.configuration.getString("csv.job.new.file.path").getOrElse("")
              val name = DateTime.now().getMillis + ".csv"
              csv.ref.moveTo(new File(path + name))
              val settings = CSVReaderSettings.Standard(linesToSkip = form.linesToSkip)
              val rows: Iterator[Array[String]] = CSVReader(path + name)(settings)
              val firstRow = rows.next()
              val test = firstRow match {
                case xs if xs.size == 0 || xs.size == 1 => xs.mkString
                case xs if xs.size > 1 => xs.mkString(", ")
              }
              Future(Ok(test))           
          }.getOrElse(Future(BadRequest("ahadasda")))
        }
      )
      result
  }

路线:

POST          /upload        @controllers.Application.upload

我在控制器之前使用@,因为我在服务类中使用带有 guice 的 DI。由于我们将使用 javascript 进行上传,因此我们需要定义我们的 jsRoutes:

js路线:

def javascriptRoutes = Action {
    implicit request =>
      import routes.javascript._
      Ok(
        Routes.javascriptRouter("jsRoutes")(
          Application.upload
        )
      ).as("text/javascript")
  }

请记住在要使用路由的模板中导入:

<script type="text/javascript" src="@routes.Application.javascriptRoutes"></script>
<script src="@routes.Assets.at("javascripts/app.js")@Messages("js.version")" type="text/javascript" ></script>

在我的视图模板中,我有一个常规的帮助表单。我做了一些 css 样式的东西来改变上传按钮和文件选择器的外观。但是输入字段在那里。

index.scala.html:

<div class="csvContainer">

    @helper.form(action = routes.Application.upload, 'enctype -> "multipart/form-data", 'id -> "csvUpload") {
        @Messages("upload.row.skip")
        @inputText(uploadForm("linesToSkip"), 'class -> "hidden")

        <div style="position:relative;">
            <div id="csvFile" style="position:absolute;">
                @Messages("upload.choose")
            </div>
            <input id="uploadFile" type="file" name="csvFile" style="opacity:0; z-index:1;" onchange="document.getElementById('csvFile').innerHTML = this.value;" />
        </div>
        <p>
            <input type="submit" value="@Messages("upload.submit")">
        </p>
    }
    </div>

app.js中是 ajax 魔法发生的地方,记住我还没有实现任何验证或很酷的 html5 东西,因为进度条和其他处理程序,在 besiors链接中描述。我使用常规的 JQuery。

应用程序.js:

$('#uploadFile').change(function(){
        var name = $(this).val().split("\\");
        console.log(name[2]);
        $('#csvFile').text(name[2]);
    });

    $('#csvFile').click(function(){
        $('#uploadFile').click();
    });

$("#csvUpload").submit(function(e) {
        e.preventDefault();
        var formData = new FormData();
        formData.append('csvFile',  $( '#uploadFile' )[0].files[0]);
        formData.append('linesToSkip', $( "#linesToSkip").val());
        jsRoutes.controllers.Application.upload().ajax({
            data: formData,
            processData: false,
            contentType: false,
            cache: false,
            type: 'POST',
            success: function(data){
                alert(data);
            }
        });
    });

我删除了很多代码来简化这个例子,我希望我没有忘记任何东西。希望这可以帮助!

于 2013-11-20T10:49:07.883 回答