Play Framework 2.1 RC1中的AJAX文件上传提供了一个空文件

时间:2013-01-07 02:47:08

标签: ajax json scala file-upload playframework

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响应(&#34;文件已上传&#34;消息在内),并作为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,而Action使用parse.multipartFormData作为参数,而不是parse.temporaryFile,可以很好地工作。

任何帮助将非常感谢。提前谢谢。

2 个答案:

答案 0 :(得分:3)

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

原因:在标准JavaScript上传文件时,使用AJAX 是不可能的由于安全限制而且有一些技巧可以解决这个问题,主要是使用iFrames,但我什么也看不见在bootstrap-ajax的代码中类似,所以可能需要修改它或使用其他解决方案。

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

通常,HTML5支持文件上传比早期版本的HTML更好,因此您可以轻松构建上传器而无需使用其他插件,请查看this topic。正如您所看到的,它提供了在上传之前验证某些数据的可能性,并且还提供了进度条。

答案 1 :(得分:1)

我正在尝试实现这样的东西,我得到了第一个版本。我就是这样做的:

在我的Controller中,我定义了一种上传文件的方法。在我的情况下,我使用Action.async,因为我使用reactivemongo将东西保存到我的MongoDB。我已删除该代码,因此不会使此示例复杂化。

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

我使用mighty csv进行csv解析。伟大的LIB!

<强>应用

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

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

<强> jsRoutes:

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 link中有所描述。 我使用常规的JQuery。

<强> app.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);
            }
        });
    });

我已经删除了很多代码来简化这个例子,我希望我没有忘记任何事情。希望这有帮助!