iOS Question Can't send a file to NODE JS using postmultipart

WhiteWizard

Member
Licensed User
I'm using this code to send a file to my NODE server:

Upload File:
Sub EnviarArchivo(posturl As String)
    Dim j As HttpJob
    Dim NameValues As Map
    
        NameValues.Initialize
        
            
        j.Initialize("", Me)
        Dim fd As MultipartFileData
        fd.Initialize
        fd.KeyName = "profile"
        fd.Dir = File.DirAssets
        fd.FileName = "1.jpg"
        fd.ContentType = "image/jpg"
        Log("Archivo:" & fd.Dir & "/" & fd.FileName)

        NameValues.Put("profile",fd.Dir & "/" & fd.FileName)

        
        j.PostMultipart(posturl, NameValues, Array(fd))
       ' j.GetRequest.SetHeader("profile", fd.Dir & "/" & fd.FileName) ' or whatever you need to set. Do it here
        Wait For (j) JobDone(j As HttpJob)
        If j.Success Then
            Log(j.GetString)
        End If
        j.Release
        
End Sub
this code returns error: ResponseError: request completed with error, status code: 500
However no file is saved in the server.
It needs to emulate POSTMAN behaviour like the picture below:
1624403486029.png

Can the reason for this error be the NODE JS code?, cos' it only accept one file:
Server Code:
const express = require('express');
const multer = require('multer');
const path = require("path");

const router = express.Router();
const moduloSQL = require('C:/AppsV2/sqlConfig')
const sql = require('mssql')

// https://www.youtube.com/watch?v=jn3tYh2QQ-g
// Compartir la carpeta:
// app.use('/profile', express.static('upload/images'));


const storage = multer.diskStorage({
    destination: './upload/images',
    filename: (req, file, cb) => {
        return cb(null, `${file.fieldname}_${Date.now()}${path.extname(file.originalname)}`)
    }
})

// FileSize en es bytes
const upload = multer({
    storage: storage,
    limits: {
        fileSize: 30000
    }
});

router.post('/',upload.single('profile'),(req, res) => {

  //console.log("Parametro req.body.file:" & req.file.filename);
  res.json({
       success: 1,
       profile_url: `http://localhost:4000/profile/${req.file.filename}`
   })

});


module.exports = router;
 

WhiteWizard

Member
Licensed User
Sending Null happends the same, however the 500 error response time is allmost instataneously, so I suppouse that the file is sent, but it is not found in the req.body.file as NODE spected.
 
Upvote 0

WhiteWizard

Member
Licensed User
I'm going to try to submit the image converting it to a Base64 String, there's no much diference in programming, only problem could be the size of the request.
 
Upvote 0

WhiteWizard

Member
Licensed User
If anyone needs to solve this, the final code using Base64 is:

B4X:
Private Sub TakePicture (TargetWidth As Int, TargetHeight As Int)
    Camera.TakePicture
    Wait For Camera_Complete (Success As Boolean, Image As Bitmap, VideoPath As String)
    B4XPages.GetManager.mStackOfPageIds.Add(B4XPages.GetPageId(Me))
    If Success Then
        Dim bmp As B4XBitmap = Image
        Log("TargetWidth:"&TargetWidth & ", TargetHeight:"& TargetHeight)
        bmp.Resize(TargetWidth, TargetHeight, True)
        GrabarImagen(bmp)
        CallSubDelayed3(Me, "Image_Available", True, bmp)
    Else
        CallSubDelayed3(Me, "Image_Available", False, Null)
  
    End If
End Sub

Private Sub GrabarImagen(bmp As B4XBitmap)
    Dim Out As OutputStream
    Out = File.OpenOutput(xui.DefaultFolder, "Test.png", False)
    bmp.WriteToStream(Out, 90, "JPEG")
    Out.Close
End Sub

Private Sub SwbAceptar_Click
    Reingreso = 0
    'Dim su As StringUtils

    'Dim base64 As String = su.EncodeBase64(File.ReadBytes(ImageView1.GetBitmap))
  
    'Dim base As String = Base64EncodeDecodeImage.ImageToString2(ImageView1.GetBitmap)
    Dim base As String = Base64EncodeDecodeImage.Base64ImageToString(xui.DefaultFolder,"Test.png")
    Log(base.Length)
  
    B4XLoadingIndicator1.Show
    wait for (enviarImagen(base)) complete (result As String)
    B4XLoadingIndicator1.Hide
    Log(result)
  
    B4XPages.ClosePage(Me)
End Sub

Sub enviarImagen(pImagenBase64 As String) As ResumableSub
    Dim j As HttpJob
    'Dim parser As JSONParser
    Dim JSONGenerator As JSONGenerator
    Dim Parametros As Map
  
    Dim hayError As String
     
    hayError = ""
      
    j.Initialize("",Me)
    Parametros.Initialize
  
    'B4XLoadingIndicator1.Show
    Parametros.put("Usuario",Main.Usuario)
    Parametros.Put("Imagen",pImagenBase64)
    Parametros.Put("Observaciones",B4XFComentario.Text)
    Parametros.Put("IdOwner",IdOwner)
    Parametros.Put("Catalogo",Catalogo)
  
    JSONGenerator.Initialize(Parametros)
  
    j.Initialize("",Me)
    j.putstring( Main.Servidor & "/imagenes", JSONGenerator.ToString )
    j.GetRequest.SetContentType("application/json")       ' yes, it comes after j.poststring()
    wait for (j) JobDone (j As HttpJob)

    If j.Success Then
        'parser.Initialize(j.GetString())
        hayError = "ok" & j.GetString()
    Else
        hayError = j.ErrorMessage

    End If
    j.Release
    'B4XLoadingIndicator1.Hide
    'Log("Cargado:"&LCuotasPrestamos)
    Return hayError
End Sub

My first idea was to send the Bitmap in raw format, without passing by the device storage, but that would mind to send the picture in PNG format. This is huge, and converting it to Base64 makes it far worse (since the size of the Base64 string is Heigth * Width * Dep (32bits) * 2). Sending a picture like this takes 1.9MB and 20 seconds.
So first I save it in JPEG format and there you can adjust the Quality parameter in WriteToStream (in PNG the quality seems to do nothing). This way the 768*360 pixel picture takes 5 seconds to the server and 3.5kb.
If you still didn't realize, my code uses the Base64EncodeDecodeImage library.
Please consider that I'm still new to B4X.

NODE JS Code:
Server Code:
// Almacena una nueva Imagen
router.put('/',(req, res,next)=> {
  var Id = req.body.Usuario;
  var imagen = req.body.Imagen;
  var texto = req.body.Observaciones;
  var idOwner = req.body.IdOwner;
  var catalogo = req.body.Catalogo;

  //var Respuesta = [{error:"0"}];

  //console.log('Id=' + Id);
  if (!Id)
  {
    res.status(400).send('Parametro invalido!');
    return;
  }

  moduloSQL.executeSQL (`SELECT IsNULL(DATEDIFF(day,(select LogInDate from Sesiones where Operador='${Id}'),getdate()),99) as tiempo`,function(err,recordset) {
  if (err)
  {
     //Respuesta[0].error = err.message;
     res.status(500).send(err.message);
     //console.log(err);
     //return err;
  }
else {
    //console.log('Columas:' + recordset.recordset[0].password + '='+pClave);
    //console.log('rows:'+recordset.rowsAffected);
    //console.log(recordset);
    if (recordset.rowsAffected > 0)
    {
      almacenarImagen(imagen,texto,idOwner,catalogo,res);
    }
    else { res.status(500).send('Sin registros');}

  }});

});

function almacenarImagen(imgData,pObservaciones,pIdOwner,pCatalogo,res) {
  var consulta = "";

  console.log("***********************************************************")
  console.log(pObservaciones);
  //console.log(pImagen.length);

//  var imgData = req.body.image;// coming from client request
//var base64Data = imgData.split(",")[1];  // split with `,`
var fileName = Date.now()+".jpeg";

  require("fs").writeFile('./upload/images/'+fileName, imgData, 'base64',
  function(err, data) {
      if (err) {
        res.status(401).send("Error grabando imagen en servidor!");
        console.log('err', err);
        } else
       res.status(200).send("ok"+fileName);
   });

  };
 
Last edited:
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
Error 500 means any server internal error. Typically, a reason is in script on server.

Check your script in any browser using this test.html (correct 'action')

B4X:
<form action="..." method="post" enctype="multipart/form-data">
  <p><input type="file" name="file">
  <p><button type="submit">Submit</button>
</form>

If upload will be successful, a problem is in B4i code. Otherwise - in server's script.
 
Upvote 0
Top