Ajax file upload without rest endpoint

92 views
Skip to first unread message

Anton Khodakivskiy

unread,
Nov 5, 2014, 12:55:45 AM11/5/14
to lif...@googlegroups.com
I was searching for a file upload example that use lift binding functionality and doesn't rely on REST endpoint but couldn't find any. Is it actually possible to implement? I tried the following snippet code, but I end up with a form without the attributes method="POST" and enctype="multipart/form-data", that are necessary to do file upload.

<div data-lift="MyUpload">
<input type="file">
<input type="submit">
</div>

class MyUpload {
def render(node: NodeSeq): NodeSeq = {

def process(): JsCmd = {
  JsCmds.Noop
}

var fphBox: Box[FileParamHolder] = Empty

val bindForm =
  "type=file" #> SHtml.fileUpload(fph => fphBox = Full(fph)) &
  "type=submit [onclick] #> SHtml.ajaxOnSubmit(process)

SHtml.ajaxForm(bindForm(node))
}
}

Thanks,

Anton

Torsten Uhlmann

unread,
Nov 5, 2014, 4:17:59 AM11/5/14
to lif...@googlegroups.com
Hey Anton,

here's a somewhat older example project by Diego, maybe it helps: http://blog.fmpwizard.com/blog/file-upload-with-lift-scala It links to the github sources.

There are probably some more example floating around.
Also, the Lift Cookbook has as section about file upload: http://chimera.labs.oreilly.com/books/1234000000030/ch03.html#_problem_33

Hope that helps,
Torsten.


--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code

---
You received this message because you are subscribed to the Google Groups "Lift" group.
To unsubscribe from this group and stop receiving emails from it, send an email to liftweb+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
AGYNAMIX(R). Passionate Software.
Inh. Torsten Uhlmann | Buchenweg 5 | 09380 Thalheim
Phone:     +49 3721 273445
Fax:         +49 3721 273446
Mobile:     +49 151 12412427
Web:        http://www.agynamix.de

Diego Medina

unread,
Nov 5, 2014, 12:10:05 PM11/5/14
to Lift
if you want an "Ajax" fileupload, it needs more love than simply changing the form to be ajax, see this wiki for a good example written by Antonio:


the file upload Torsten pointed out still works, but it is not an ajax upload :)

Thanks

Diego
Diego Medina
Lift/Scala consultant
di...@fmpwizard.com
http://fmpwizard.telegr.am

Anton Khodakivskiy

unread,
Nov 6, 2014, 2:14:45 AM11/6/14
to lif...@googlegroups.com
Diego, correct me if I'm wrong, the example on Assembla still uses regular upload technique, but wraps it into iframe, so it feels like ajax, is this right?

As I understand Lift's liftAjax.js doesn't support ajax file uploads. In other words liftAjax.js doesn't know how to construct jQuery ajax call that includes HTML5 file APIs. I just tried fixing this by using FormData HTML5 javascript API and providing my custom JQueryArtifacts. The FormData API wraps all the form fields, including file fields into an object that jQuery can then upload. But to do this we also need to specify the `dataProcessing: false` attribute on $.ajax call.

This, along with adding method="post" enctype="multipart/form-data" to the form allows the browser to upload the file via Lift AJAX. But then the server hicks up on the request processing. The logs are also below. Do you have an idea what might be causing an error on the server side? Is it something that can be easily fixed?

In snippet:

    def process() = { ... }

    val onChange = SHtml.makeAjaxCall(JsRaw(s"new FormData(this.form)"))

    val bindForm =
      "type=file" #> SHtml.fileUpload(fph => fphBox = Full(fph), "onchange" -> onChange.toJsCmd) &
      "type=hidden" #> SHtml.onSubmitUnit(process)

      bindForm(
        <lift:form.ajax enctype="multipart/form-data" method="post">
          <input type="file"></input>
          <input type="hidden"></input>
        </lift:form.ajax>)


    "type=submit [onclick]" #> SHtml.makeAjaxCall(JsRaw(s"new FormData(this.form)"))

In Boot:

    LiftRules.jsArtifacts = new JQueryArtifacts {
      override def ajax(data: AjaxInfo): String = {
        "jQuery.ajax(" + toJson(data, S.contextPath,
          prefix =>
            JsRaw("liftAjax.addPageNameAndVersion(" + S.encodeURL(prefix + "/" + LiftRules.ajaxPath + "/").encJs + ", version)")) + ");"
      }

      private def toJson(info: AjaxInfo, server: String, path: String => JsExp): String =
        (("url : " + path(server).toJsCmd) ::
          "data : " + info.data.toJsCmd ::
          "processData : " + JsRaw("!(" + info.data.toJsCmd + " instanceof FormData)").toJsCmd ::
          ("type : " + info.action.encJs) ::
          ("dataType : " + info.dataType.encJs) ::
          "timeout : " + info.timeout ::
          "cache : " + info.cache :: Nil) ++
          info.successFunc.map("success : " + _).toList ++
          info.failFunc.map("error : " + _).toList mkString ("{ ", ", ", " }")
    }

Error log:

2014-11-05 22:03:30,456 [qtp1728293208-163032] ERROR net.liftweb.http.LiftRules - Exception being returned to browser when processing /ajax_request/F847084957972BAZTO3-00/
java.lang.IllegalArgumentException: !hex:f4
    at org.eclipse.jetty.util.TypeUtil.convertHexDigit(TypeUtil.java:369)
    at org.eclipse.jetty.util.UrlEncoded.decodeUtf8To(UrlEncoded.java:511)
    at org.eclipse.jetty.util.UrlEncoded.decodeTo(UrlEncoded.java:565)
    at org.eclipse.jetty.server.Request.extractParameters(Request.java:290)
    at org.eclipse.jetty.server.Request.getParameterNames(Request.java:723)
    at net.liftweb.http.provider.servlet.HTTPRequestServlet.params$lzycompute(HTTPRequestServlet.scala:80)
    at net.liftweb.http.provider.servlet.HTTPRequestServlet.params(HTTPRequestServlet.scala:80)
    at net.liftweb.http.Req$$anonfun$11.apply(Req.scala:493)
    at net.liftweb.http.Req$$anonfun$11.apply(Req.scala:452)
    at net.liftweb.http.AvoidGAL.thunk$lzycompute(Req.scala:355)
    at net.liftweb.http.AvoidGAL.thunk(Req.scala:355)
    at net.liftweb.http.Req$$anonfun$18.apply(Req.scala:506)
    at net.liftweb.http.Req$$anonfun$18.apply(Req.scala:505)
    at net.liftweb.http.Req.x$49$lzycompute(Req.scala:965)
    at net.liftweb.http.Req.x$49(Req.scala:961)
    at net.liftweb.http.Req.__params$lzycompute(Req.scala:962)
    at net.liftweb.http.Req.__params(Req.scala:962)
    at net.liftweb.http.Req._params(Req.scala:939)
    at net.liftweb.http.Req.params$lzycompute(Req.scala:969)
    at net.liftweb.http.Req.params(Req.scala:969)

Antonio Salazar Cardozo

unread,
Nov 7, 2014, 11:36:18 AM11/7/14
to lif...@googlegroups.com
I know someone's gotten this working before… This seems like the parameter isn't being properly uploaded
as a file, if I were to guess based on no additional information :)

help you further.
Thanks,
Antonio
Reply all
Reply to author
Forward
0 new messages