我正在使用带有 Shrine gem 的 Rails 5.2 来上传图片。在客户端,我使用 NativeScript 6.0 和 Angular 8.0。
我已经安装了 Shrine,它在 Rails 端工作并通过 Uppy 直接上传。
在使用 NativeScript 的前端(Android 手机)上,我可以拍摄照片(使用 nativescript-camera)并使用 nativescript-background-http 将其发送到 nativescript-background-http 演示服务器(基于节点)。
我遇到的问题是从 NativeScript 发送到 Shrine。
在后端我有这些路线
Rails.application.routes.draw do
resources :asset_items
mount ImageUploader.upload_endpoint(:cache) => "/images/upload" # POST /images/upload
end
在我的神社设置中,我有
require "shrine"
require "shrine/storage/file_system"
Shrine.storages = {
cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
store: Shrine::Storage::FileSystem.new("public", prefix: "uploads"), }
Shrine.plugin :logging, logger: Rails.logger
Shrine.plugin :upload_endpoint
Shrine.plugin :activerecord
Shrine.plugin :cached_attachment_data
Shrine.plugin :restore_cached_data
在前端
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
处理程序看起来像这样
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")
}
当我拍照时,它会尝试将其发送到后端,并在服务器上获得以下日志;
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
我认为问题出在我发送到 Shrine 的标题中,将图像从 NativeScript 发送到 Shine 的正确方法是什么?
更新:尝试 multipartUpload
我尝试了 multipartUpload 并且还得到了 400 错误代码
// 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);
**更新:尝试 nativescript-background-http 演示服务器
这行得通,我可以成功地将 multipartUpload 发送到演示服务器
更新:记录请求的中间件
开始整理一个中间件机架类来打印响应
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
当我发送文件时,它会发送响应
Middleware called. Status: 400, Headers: {"Content-Type"=>"text/plain", "Content-Length"=>"16"}
从 NativeScript 发送时查看 byebug 中的标题、状态和正文;
(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>
在 Rails 端通过 Uppy 成功发送会显示此结果
(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>
来自 Chrome 网络的关于通过 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)
更新:可以使用 blob 通过 Angular 上传
我可以使用 blob 从 Angular 8.0 上传到 Shrine。
sendImage(files: FileList){
this.image = files.item(0);
var directUrl = "http://localhost:9000/images/upload";
// Create a formData object
const formData: FormData = new FormData();
formData.append('file', files.item(0), this.image.name);
// Direct Upload
this.http.post(directUrl, formData).subscribe(event => {
console.log("Successfully uploaded: " + event);
this.asset.image = JSON.stringify(event);
});
}
更新:尝试 nativescript-http-formdata
这是我通过nativescript-http-formdata发送图片的实现;
async sendPicture(filepath, imageAsset) {
var url = "http://localhost:9000/images/upload";
var name = filepath.substr(filepath.lastIndexOf("/") + 1);
// Get bitmap of file
const imageAndroidBitmap = android.graphics.BitmapFactory.decodeFile(filepath);
// Prepare the formdata
let fd = new TNSHttpFormData();
let param: TNSHttpFormDataParam = {
data: imageAndroidBitmap,
contentType: 'image/jpeg',
fileName: 'test.jpg',
parameterName: 'file1'
};
let params = [];
params.push(param);
try {
const response: TNSHttpFormDataResponse = await fd.post(url, params, {
headers: {}
});
console.log(response);
} catch (e) {
console.log(e);
}
错误:查看此错误后,我认为 okhttp3 已加载,但 NativeScript 6.0 的语法错误
LOG from device Galaxy S8: Error: java.lang.Exception: Failed resolving method create on class okhttp3.RequestBody
更新:通过 Curl 上传到 Shrine = 作品
尝试以下并得到相同的错误请求 400 错误;
发送图片
curl -X POST --form "file=@t-bird.jpg" http://localhost:9000/images/upload
{"id":"4b4d42e77b4fa7ecddbd93cd07845cc2.jpg","storage":"cache","metadata":{"filename":"t-bird.jpg","size":1478512,"mime_type":"image/jpeg"}}
*NOTE: when we send the picture we use 'file' instead of 'image'*
发送文本表单(可选)
curl -X POST -d "asset_item[name]=curl" http://localhost:9000/asset_items.json
将输出转换为 JSON
irb
{id:"7276dc618cdd23bf3f5a9243d3c59399.jpg",storage:"cache",metadata:{filename:"t-bird.jpg",size:1478512,mime_type:"image/jpeg"}}.to_json
结果
"{\"id\":\"7276dc618cdd23bf3f5a9243d3c59399.jpg\",\"storage\":\"cache\",\"metadata\":{\"filename\":\"t-bird.jpg\",\"size\":1478512,\"mime_type\":\"image/jpeg\"}}"
带有图像数据的 POST 文本
curl -X POST -d "asset_item[name]=curl" -d 'asset_item[image]="{\"id\":\"7276dc618cdd23bf3f5a9243d3c59399.jpg\",\"storage\":\"cache\",\"metadata\":{\"filename\":\"t-bird.jpg\",\"size\":1478512,\"mime_type\":\"image/jpeg\"}}"' http://localhost:9000/asset_items.json
电流通过
我认为我目前最好的机会是使用 nativescript-background-http 以正确的格式将多部分帖子发送到神社。那是什么。