Upload json file from js frontend to zhttp backend

142 views Asked by At

I have js code for upload json file with content

{"name": "John", "age": 35}

from frontend js with using POST to scala zhttp backend. JS code:

    <script>
        async function loadJsonTests() {
             console.log('Begin loading new JSON file with tests into server.');
             let formData = new FormData();
             let url = `load_test`;
             formData.append("file", fileupload.files[0]);
                 let response = await fetch(url, {
                   method: "POST",
                   headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                   },
                   body: formData
                   //body: JSON.stringify({name: 'John', age: 35})
                 });
             return response.json();
        }

        async function btnUploadTestFile() {
             const result = await loadJsonTests();
             console.log(result);
        }
    </script>

html for upload file and call js function

            <td width="70%" height="200px">
                <input id="fileupload" type="file" name="fileupload" />
                <br>
                <div id="btn_upload_test_file" onclick="btnUploadTestFile()">send json file</div>
            </td>

Scala build.sbt part

ThisBuild / scalaVersion := "2.12.15"

  val Versions = new {
    val zio        = "2.0.5"
    val zio_config = "3.0.7"
    val z_http     = "2.0.0-RC11"
    val zio_json   = "0.3.0-RC11"
  }
  lazy val dependencies =
    new {
      val zio = "dev.zio" %% "zio" % Versions.zio

      val zio_conf          = "dev.zio" %% "zio-config"          % Versions.zio_config
      val zio_conf_typesafe = "dev.zio" %% "zio-config-typesafe" % Versions.zio_config
      val zio_conf_magnolia = "dev.zio" %% "zio-config-magnolia" % Versions.zio_config

      val z_http = "io.d11" %% "zhttp" % Versions.z_http

      val zio_json = "dev.zio"       %% "zio-json"       % Versions.zio_json

      val zioDep = List(zio, zio_conf,zio_conf_typesafe,zio_conf_magnolia, z_http, zio_json )
    }

Scala backend code

  def apply(): Http[Any, Throwable, Request, Response] =
    Http.collectZIO[Request] {
      case req@(Method.POST -> !! / "load_test") => loadTest(req)
    }

  def loadTest(req: Request): ZIO[Any, Throwable, Response] =
    for {
      bodyStr <- req.body.asString
      _ <- ZIO.logInfo(s"req JSON str = $bodyStr")
      u <- req.body.asString.map(_.fromJson[User])
      resp <- u match {
        case Left(e) =>
          ZIO.debug(s"Failed to parse the input: $e").as(
            Response.text(e).setStatus(Status.BadRequest)
          )
        case Right(u) =>
          ZIO.succeed(Response.json(u.toJson))
      }
    } yield resp

Whan I do post request from js I have next error:

message="req JSON str = ------WebKitFormBoundary11BJkIQrpt39tJXd
Content-Disposition: form-data; name="file"; filename="tests_set_1.json"
Content-Type: application/json

{"name": "John", "age": 35}
------WebKitFormBoundary11BJkIQrpt39tJXd--
" location=webui.WebUiApp.loadTest file=WebUiApp.scala line=51
Failed to parse the input: (expected '{' got '-')

How I can extract only json content from body, without additional information ------WebKitFormBoundary..... ?

If I send json from js

body: JSON.stringify({name: 'John', age: 35})

, not a file, then everything ok.

3

There are 3 answers

0
Aleksey N Yakushev On

Fixed with next js code:

    <script>
        function loadFile(file){
            return new Promise(resolve=>{
                let reader = new FileReader()
                reader.onload = function(event) {
                    let data = event.target.result
                    resolve(data)
                }
                reader.readAsText(file)
            })
        }

        async function loadJsonTests() {
             console.log('Begin loading new JSON file with tests into server.');
             var file = fileupload.files[0];
             var fileContent = await loadFile(file);
             console.log('-- request ----------------------------------------------');
             console.log(fileContent);
             console.log('---------------------------------------------------------');
              const response = await fetch(`load_test`, {
                   method: "POST",
                   headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                   },
                   body: fileContent
                 });
             return response.json();
        }

        async function btnUploadTestFile() {
             const resultJson = await loadJsonTests();
             console.log('-- response ---------------------------------------------');
             console.log(resultJson);
             console.log('---------------------------------------------------------');
        }
    </script>
0
Gaël J On

Your backend expects a plain body as a string that you then parse as a JSON.

But your frontend is sending form data with a file, that's a different type of content in the HTTP request.

Either your backend must be modified to read formdata (instead of a plain string), or in your frontend read the file content and send only the file content.

0
Thallius On

You can use the FileReader to get the contents of the file and then send the content only

https://developer.mozilla.org/en-US/docs/Web/API/FileReader?retiredLocale=en

fileInputElement.change(function(e) {
  var reader = new FileReader();
  reader.onload = function(e) {
    console.log(reader.result);
  }
  reader.readAsText(fileInput[0].files[0]);   
});