Sunday, 14 May 2017

Need some guidance/opinions on backend image handling + form data submit for react native app

I'm making my first react native app and also, the first time I'm doing a backend for such a thing so my experience is very limited, I'm a front-end developer so I feel i'm doing something wrong here, definitely doesn't look like the best way to handle this particular situation so I would like to have some opinions and possibly some guidance on how I can achieve this the best way possible.

Problem?

I need to let the user send a report to backend, save it on DB. But this involves among the common fields, pick an image from their phone and be able to upload it with all the other data from the form.

Sounds pretty straight-forward.

For server, I'm using NodeJS with Express, and for DB using Mongoose and MongoDB.

My current implemented solution:

Open the modal with the form, type in all the info there, let the user pick the photo from their phone, after filling all up, by clicking submit it does the following:

  • Verifies all the form required fields are set, if not, returns alert message.
  • Then, first, I upload the image to server -> server returns the HTTP URL of the image, if all succeeds, by having that image URL that i needed, gets all the form data and I do a POST HTTP fetch() with the entire info containing image URL i just got.

Now this is full of promises with nested .then() and .catch() everywhere.

Now here's my React Native front-end code.

    _pickImage: function() {
            imagePick((source, data) => this.setState({ imageThumbnail: source, imageUploadData: data}));
    },


    _onSubmitData: function() {
        this._saveAllFormData()
    },

    _saveAllFormData: function() {
        var fieldErrors = this._validateMissingFields();

        if(fieldErrors.length > 0) {
            this._showMissingFieldsError(fieldErrors);
            return;
        }

        uploadFile([
          { name: 'uploaded_picture',
            type: 'image/png',
            filename: 'uploaded_picture.png',
            data: this.state.imageUploadData
          }
        ])
        .then(res => {
            var data = JSON.parse(res.data);
            this.setState(
                {
                  imageUrl: data.image_url,
                  imageName: data.image_name
                }
            );

            this._sendFormDataToDB()
                .then(() => {
                    console.log('Your report has been successfully saved!');
                })
                .catch((err) => {
                    console.log('WROOONG!');
                });
        })
        .catch(err => console.log(err))
    },

    _sendFormDataToDB: function(data) {
        return new Promise((resolve, reject) => {
            var url = Constants.API_URL + '/reports/save';
            fetch(url, {
                method: 'POST',
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    status: 'missing',
                    image_url: this.state.imageUrl,
                    image_name: this.state.imageName,
                    date_posted: new Date(),
                    date_lts: moment(this.state.date).format(),
                    location: this.state.location,
                    description: this.state.description,
                    country: this.props.countryName,
                    approved: false
                })
            })
            .then((response) => response.json())
            .then((responseJson) => {
                console.log(responseJson);
                resolve();
            })
            .catch((error) => {
                alert('There was a problem trying to save the report. Check your connection and try again.');
                reject();
                //XXX: Do rollback and delete image from server
            })
        })
    },

And this is my server side method:

// uploads the image
app.route('/upload')
    .post(function (req, res, next) {
        var fstream;
        var readStream;
        var serverObject = server.address();
        var serverUrl = serverObject.address + ':' + serverObject.port;

        req.pipe(req.busboy);
        req.busboy.on('file', function (fieldname, file, filename) {
            console.log("Uploading: " + filename);

            //Path where image will be uploaded
            var filename = new Date().getTime() + '_' + filename;
            fstream = fs.createWriteStream(__dirname + '/public/images/' + filename);
            file.pipe(fstream);
            fstream.on('close', function () {
                console.log("Upload Finished of " + filename);
                gm(__dirname + '/public/images/' + filename)
                .resize('400', '300', '^')
                .gravity('Center')
                .crop('400', '300')
                .write(__dirname + '/public/images/' + filename, function(err) {
                  if (!err) {
                      console.log('Resized image successfully: ' + filename);
                      res.json(
                        {
                            image_url: serverUrl + '/images/' + filename,
                            image_name:  filename
                        });
                  }
                });
            });
        });
    }
);

// Saves a report
app.post('/api/reports/save', function(req, res) {
    var report = req.body;

    Report.addReport(report, function(err, report) {
        if(err) {
            console.log(err);
        }

        res.json(report);
    });
})

The problem that I saw with my current implementation is that if anything goes wrong after image is uploaded and tries to save the form data to DB, there's an already created image by a user in my server. So if that happens, I guess I should have a rollback implementation to handle image deletion from server, right?

And since I'm feeding my app with all those external reports that are in a Server, the image URLs needs to be absolute paths.

I apologies for the long post. Any suggestion is really appreciated.



via msqar

No comments:

Post a Comment