Monday, 19 August 2019

Upload to Rails Shrine from NativeScript

I'm using Rails 5.2 with the Shrine gem for image upload. On the client side I'm using NativeScript 6.0 with Angular 8.0.

I've installed Shrine and have got it working on the Rails side and also have setup Direct image upload and tested that on the backend with Uppy and it works.

On the frontend (Android mobile) when using NativeScript I can take a photo (using nativescript-camera) and send it using nativescript-background-http to the nativescript-background-http demo server, this works.

The problem I have is getting the correct settings to send from NativeScript to Shrine.

On the backend I have these routes

Rails.application.routes.draw do
  resources :asset_items
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
  mount ImageUploader.upload_endpoint(:cache) => "/images/upload" # POST /images/upload
end

In my shrine setup I have

require "shrine"
require "shrine/storage/file_system"

Shrine.storages = {
  cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"), # temporary
  store: Shrine::Storage::FileSystem.new("public", prefix: "uploads"),       # permanent
}

Shrine.plugin :logging, logger: Rails.logger
Shrine.plugin :upload_endpoint
Shrine.plugin :activerecord
Shrine.plugin :cached_attachment_data # for retaining the cached file across form redisplays
Shrine.plugin :restore_cached_data # re-extract metadata when attaching a cached file
#Shrine.plugin :rack_file # for non-Rails apps

On the frontend

onTakePictureTap(args) { requestPermissions().then( () => { var imageModule = require("tns-core-modules/ui/image");

            takePicture({width: 150, height: 100, keepAspectRatio: true})
                .then((imageAsset: any) => {
                    this.cameraImage = imageAsset;
                    let image = new imageModule.Image();
                    image.src = imageAsset;
                    this._dataItem.picture_url = this.imageAssetURL(imageAsset);

                    // Send picture to backend
                    var file =  this._dataItem.picture_url;
                    var url = "https://192.168.1.4/images/upload";
                    var name = file.substr(file.lastIndexOf("/") + 1);

                    // upload configuration
                    var bghttp = require("nativescript-background-http");
                    var session = bghttp.session("image-upload");
                    var request = {
                        url: url,
                        method: "POST",
                        headers: {
                            "Content-Type": "application/octet-stream"
                        },
                        description: "Uploading " + name
                    };

                    var task = session.uploadFile(file, request);

                    task.on("error", this.errorHandler);
                    task.on("responded", this.respondedHandler);
                    task.on("complete", this.completeHandler);

                }, (error) => {
                    console.log("Error: " + error);
                });
        },
        () => alert('permissions rejected')
    );
} // onTakePictureTap

The handlers look like this

errorHandler(e) {
    alert("received " + e.responseCode + " code.");
    var serverResponse = e.response;
}

respondedHandler(e) {
    alert("received " + e.responseCode + " code. Server sent: " + e.data);
}

completeHandler(e) {
    alert("received")
}

When I take a picture it tries to send it to the backend and I get the following information on the server;

Started POST "/images/upload" for 103.232.216.30 at 2019-08-14 11:14:09 +1000
Cannot render console from 103.232.216.30! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255

I think the problem is in the headers I'm sending to Shrine, what is the correct way of sending the image to shrine from NativeScript?

Update: Try multipartUpload

I tried the multipartUpload and also got a 400 error code

                    // upload configuration
                    var bghttp = require("nativescript-background-http");
                    var session = bghttp.session("image-upload");
                    var request = {
                        url: url,
                        method: "POST",
                        headers: {
                            "Content-Type": "application/octet-stream"
                        },
                        description: "Uploading " + name
                    };

                    var params = [
                        {
                            name: "fileToUpload.jpg",
                            filename: file,
                            mimeType: "image/jpeg"
                        }
                    ];

                    var task = session.multipartUpload(params, request);

Update: middleware to log request

Started to put together a middleware rack class to print out the response

class MyMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
    puts "Middleware called. Status: #{status}, Headers: #{headers}"
    [status, headers, body]
  end
end

When I send a file, it sends the response

Middleware called. Status: 400, Headers: {"Content-Type"=>"text/plain", "Content-Length"=>"16"}

Having a look at headers,status and body in byebug when sending from NativeScript;

(byebug) headers
{"Content-Type"=>"text/plain", "Content-Length"=>"16", "Cache-Control"=>"no-cache", "X-Request-Id"=>"7a5d40e2-5c09-4fc7-88b5-83813cedf20e", "X-Runtime"=>"0.055892"}
(byebug) status
400
(byebug) body
#<Rack::BodyProxy:0x000056471192c580 @body=#<Rack::BodyProxy:0x000056471192c620 @body=#<Rack::BodyProxy:0x000056471192c878 @body=#<Rack::BodyProxy:0x000056471192c968 @body=#<Rack::BodyProxy:0x000056471192cdc8 @body=["Upload Not Found"], @block=#<Proc:0x000056471192cd28@/usr/local/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/rack-2.0.7/lib/rack/tempfile_reaper.rb:16>, @closed=false>, @block=#<Proc:0x000056471192c918@/usr/local/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/actionpack-5.2.3/lib/action_dispatch/middleware/executor.rb:15>, @closed=false>, @block=#<Proc:0x000056471192c850@/usr/local/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.2.3/lib/rails/rack/logger.rb:39>, @closed=false>, @block=#<Proc:0x000056471192c5d0@/usr/local/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/cache/strategy/local_cache_middleware.rb:30>, @closed=false>, @block=#<Proc:0x000056471192c508@/usr/local/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/actionpack-5.2.3/lib/action_dispatch/middleware/executor.rb:15>, @closed=false>

A successful send via Uppy on the Rails side shows this result

(byebug) status
200
(byebug) headers
{"Content-Type"=>"application/json; charset=utf-8", "Content-Length"=>"149", "ETag"=>"W/\"29040a3f35783193f7ba450aac8906bd\"", "Cache-Control"=>"max-age=0, private, must-revalidate", "X-Request-Id"=>"53b380b8-e902-49d3-885f-634fc9ea82dc", "X-Runtime"=>"0.028117"}
(byebug) body
#<Rack::BodyProxy:0x00007f2c801f8868 @body=#<Rack::BodyProxy:0x00007f2c801f8980 @body=#<Rack::BodyProxy:0x00007f2c801f8de0 @body=#<Rack::BodyProxy:0x00007f2c801f90b0 @body=#<Rack::BodyProxy:0x00007f2c801f98f8 @body=["{\"id\":\"85bf685af3b7965c701227478e2189a2.jpg\",\"storage\":\"cache\",\"metadata\":{\"filename\":\"DSCF3107_edited.JPG\",\"size\":3998332,\"mime_type\":\"image/jpeg\"}}"], @block=#<Proc:0x00007f2c801f9858@/usr/local/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/rack-2.0.7/lib/rack/etag.rb:30>, @closed=false>, @block=#<Proc:0x00007f2c801f8f98@/usr/local/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/actionpack-5.2.3/lib/action_dispatch/middleware/executor.rb:15>, @closed=false>, @block=#<Proc:0x00007f2c801f8db8@/usr/local/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.2.3/lib/rails/rack/logger.rb:39>, @closed=false>, @block=#<Proc:0x00007f2c801f8890@/usr/local/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/activesupport-5.2.3/lib/active_support/cache/strategy/local_cache_middleware.rb:30>, @closed=false>, @block=#<Proc:0x00007f2c801f8750@/usr/local/rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/actionpack-5.2.3/lib/action_dispatch/middleware/executor.rb:15>, @closed=false>

Info from Chrome Network on Successful upload via Uppy

      - General
        : Request URL: http://localhost:9000/images/upload
        : Request Method: POST
        : Status Code: 200 OK
        : Remote Address: [::1]:9000
        : Referrer Policy: strict-origin-when-cross-origin

      - Response                
        : Cache-Control: max-age=0, private, must-revalidate
        : Content-Length: 141
        : Content-Type: application/json; charset=utf-8
        : ETag: W/"8e3a470866888e1d724013e95d0a49b4"
        : X-Request-Id: 3e4222bd-e5bf-4270-bc31-1fc2c25696b1
        : X-Runtime: 0.010884

      - Request
        : Accept: */*
        : Accept-Encoding: gzip, deflate, br
        : Accept-Language: en-US,en;q=0.9
        : Cache-Control: no-cache
        : Connection: keep-alive
        : Content-Length: 110221
        : Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryBRJtv5UR0QTM2J2x
        : Cookie: _session_id=73b3a497c62bd745a789bc00b9f14361; org.cups.sid=c9eb7594a0515f4965b7a8e2f7900050; io=aArI7Q_64r2LWkc5AAAA; CSRF-Token-4MYJC=hLjA49c9bSsUhMUrYMfgSFSEnquQufo3; CSRF-Token-CAGDA=53tpJXxkvAstfeCoAKKbWgQDiQpU7xLj; CSRF-Token-TUFRR=kAWjSsQW4YCdEyGtaNKpfPT4gjToabYL; XSRF-TOKEN=HCjw%2B3WTJcSd1ddt45JGGGo8Uer43ggZZRrcsLc2NFgTdghJ852fqo0rWUx0%2FfBIOfv9YEMJ7mXw8TCix7d2cA%3D%3D; CSRF-Token-XDZDE=LyXXMXei6ci6FHrE3MfTxn3ARAKXYgMZ; _personal_property_rails_prototype_session=u65TkCvL9slUmGQQsP37lJH0BPcMw0E5%2FaDNw6frbuFw8NwqfM9gYPp%2F%2F830NFeZJqwxnYqc%2FCP%2FPIXhvPGFbD4waESKMKS1ChILCxTXZAPRFFULtu9m4Xl2G6AlF0ZamkzY7sdcE15vnpIBm8M%3D--98yhZGLNKsL5dnSX--Radl4qCShjACiTHc5UTH1A%3D%3D
        : Host: localhost:9000
        : Origin: http://localhost:9000
        : Pragma: no-cache
        : Referer: http://localhost:9000/asset_items/new
        : User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36

      - Form data
        : name: 2014-mlug.png
        : type: image/png
        : files[]: (binary)



from Upload to Rails Shrine from NativeScript

No comments:

Post a Comment