I have an image saved on a MongoDB. The model is the following:
picture: {
metadata: {
name: { type: String, default: null },
comment: { type: String, default: null },
publisherID: { type: String,default: null },
date: { type: Date, default: Date.now },
size: { type: Number,default: 0 },
type: { type: String, default: null }
},
data: { type: Buffer, default: null },
tags: Array
}
Now I need to load the image again from the DB.
I make an AJAX call and request the picture with the id.
$.ajax({
type: "POST",
url: window.location.origin + '/picture',
contentType: 'application/json',
dataType: 'json',
async: true,
data: JSON.stringify({ id: id }),
success: function (result) {
console.log(result);
a = result;
var img = result.result[0].picture.data.join("").toString('base64');
img = "data:" + result.result[0].picture.metadata.type + ";base64," + img;
$('#img').attr('src', img);
},
error: function (jqXHR, textStatus, errorThrown) {
console.log('error ' + textStatus + " " + errorThrown);
success = false;
}
});
And this is the handler on the server
var Picture = require('../models/picture');
Picture.find({ "_id": req.body.id}, function (err, pic) {
if (err || !pic)
res.end(JSON.stringify({ result: "error" }));
if (pic) {
console.log(pic);
res.end(JSON.stringify({ result: pic }));
}
})
I have translated the binary data into base64 but the image doesnt display.
(I had to join the binary data because they came into an array).
There are some other similar posts however they dont have anything that I havent done (I think).
As stated in the comments, it is better to have a separate endpoint in your application to make these calls "look like" standard static file requests. So the first thing I would do is change your schema a little:
picture: {
metadata: {
name: { type: String, default: null },
comment: { type: String, default: null },
publisherID: { type: String,default: null },
date: { type: Date, default: Date.now },
size: { type: Number,default: 0 },
type: { type: String, default: null }
},
path: { type: String, required: true },
mime: { type: String, required: true },
data: { type: Buffer, default: null },
tags: Array
}
So that adds two fields which are going to identify the "path" to the image to match, and "mime" as the mime-type of the file. So "path" is a more "friendly" identifier than an _id and the "mime-type" would be set in insert to match the returned content type.
Then you set up a route to serve the content:
app.get('/images/:imgname', function(req,res) {
Picture.find({ "picture.path": req.param("imgname") }, function(err,pic) {
if (err) // checking here
// Sending response
res.set('Content-Type', pic.mime);
res.send( pic[0].picture.data );
});
})
So when you did a request like:
wget http://localhost:3000/images/test.png
This would happen:
Find the document matcing "path" for "test.png"
Assign the document property for "picture.mime" as the Content-Type for the response
Send the binary data back as the response
So for the client, it's an actual file as the response, and the point is the that "browser" can cache this and not hit your application where the "cached" copy is valid.
If you are embedding Base64 encoded data in JSON responses then you loose that important part and you send the data every time. It's also a very messy process to handle, as you have discovered.
Related
I need to load a datatable with a JSON dataset. I don't want to do server side processing. but I do want to retrieve the full set of data from a URL. I got to the point where there's no error on load but for some reason, the table stays empty with 0 results.
I am building the JSON on the server side like this:
public static function datatableOutput($array){
foreach($array as $key=>$line){
$output[] = [
"href"=>$line['href'],
"code"=>$line['code'],
"time"=>$line['time'],
"img_count"=>$line['img_count'],
"int_count"=>$line['int_count'],
"ext_count"=>$line['ext_count']
];
}
return array('data'=> $output);
}
Later in the code it returns a json string like this:
return response()->json($this::datatableOutput($links));
And in the view my script looks like this:
$.ajax({
url: '/crawl',
type: "post",
data: {
url: $("#url").val(),
pages: $("#pages").val(),
_token:"{{ csrf_token() }}"
},
dataType : 'json',
success: function(data){
// Done state
$('#iamarobotnotaslave').hide();
$('#justasecdude').hide();
$('#crawl').hide();
$('#report_section').show();
$('#report').DataTable( {
data: data,
columns: [
{ title: "href" },
{ title: "code" },
{ title: "time" },
{ title: "img_count" },
{ title: "int_count" },
{ title: "ext_count" }
]
} );
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
// Error state
$('#iamarobotnotaslave').hide();
$('#justasecdude').hide();
$('#goaheaddude').show();
$('#error').show();
}
});
The JSON data set returned looks like this:
{
"data":[
{
"href":"\/",
"code":200,
"time":0.2746608257293701,
"img_count":6,
"int_count":204,
"ext_count":6
},
{
"href":"\/feature\/automated-marketing-reports",
"code":200,
"time":0.1753251552581787,
"img_count":7,
"int_count":72,
"ext_count":6
},
{
"href":"\/feature\/marketing-dashboard-2",
"code":200,
"time":0.15781521797180176,
"img_count":8,
"int_count":73,
"ext_count":6
}
]
}
Everything seems to be valid but for some reason I still get 0 results ... I am probably missing something obvious thought. I tried the JSON with and with out the "data" array.
The error message being received is:
"User validation failed: email: Path email is required., display_name: Path display_name is required."
The error name being sent back is: ValidationError.
The code for the ajax call is:
function submit_new_user(display_name, email, password){
let user_data = new New_User(display_name, email, password);
console.log(user_data);
$.ajax ({
dataType: 'JSON',
data: user_data,
method:'POST',
url: 'http://localhost:3001/user',
success: (res)=>{
console.log(res);
},
error: (xhr, ajaxOptions, thrownError)=>{
console.log(xhr, ajaxOptions, thrownError);
}
});
};
the console.log seen above prints:
New_User {display_name: "test", email: "test#test.com", password: "Passw0rd"}
The server route is:
app.post('/user', (req, res)=>{
let body = _.pick(req.body, ['display_name', 'email', 'password']);
let user = new User(body);
user.save().then(()=>{
return user.generateAuthToken();
}).then((token)=>{
res.header('x-auth', token).send(user);
}).catch((err)=>{
res.status(400).send(err);
});
});
This route works when pinged with Postman. The server supports cors.
The model is as follows:
var UserSchema = new mongoose.Schema({
display_name: {
type: String,
unique: true,
required: true,
validate: {
validator: (val)=>{
return /^[\w\s]+$/.test(val);
},
message: '{VALUE} is not a valid display name. Only
alphanumeric, upper and lower case characters are allowed.'
}
},
email: {
type: String,
required: true,
unique: true,
validate: {
validator: validator.isEmail,
message: '{VALUE} is not a valid email.'
}
},
password: {
type: String,
require: true,
minlength: 6
},
tokens: [{
access:{
type: String,
required: true
},
token: {
type: String,
required: true
}
}]
});
I'm still learning node.js. I was guided through the creation of the API, and am trying to build a front end on my own.
The object leaving contains the three value pairs - but the object arriving, before the server changes it, is empty. I'm at loss as to why.
SOLUTION: the problem was in the server-side bodyParser. I had forgotten to include the encoded url method. It was not parsing the JSON as it came in, since previously the API had only been tested using Postman there was no need to use the .urlencoded() method.
I'm trying to send an object that looks like this
var postBody = {
id: userID,
objectID: [0, 1],
objects: [
{id: 0, x: 0.33930041152263374, y: 0.08145246913580247, width: 0.0823045267489712, height: 0.30864197530864196},
{id: 1, x: 0.5277777777777778, y: 0.08453888888888889, width: 0.0823045267489712, height: 0.30864197530864196}
]
};
this is ajax call
$.ajax({
type: 'POST',
url: url,
data: postBody,
dataType: 'JSONP',
success: function(data) {
console.log(data);
},
error: function(err) {
console.log(err);
}
});
this is what it looks like on server
{ id: '583f137fc338b517ec467643',
'objectID[]': [ '0', '1' ],
'objects[0][id]': '0',
'objects[0][x]': '0.33930041152263374',
'objects[0][y]': '0.08145246913580247',
'objects[0][width]': '0.0823045267489712',
'objects[0][height]': '0.30864197530864196',
'objects[1][id]': '1',
'objects[1][x]': '0.5277777777777778',
'objects[1][y]': '0.08453888888888889',
'objects[1][width]': '0.0823045267489712',
'objects[1][height]': '0.30864197530864196' }
if I put data: JSON.stringify(postBody) this is what I get
{ '{"id":"583f137fc338b517ec467643","objectID":[0,1],"objects":[{"id":0,"x":0.5,"y":0.5,"width":0.1,"height":0.1},{"id":1,"x":0.5401234567901234,"y":0.1833043209876543,"width":0.0823045267489712,"height":0.30864197530864196}]}': '' }
And it works! but then I cannot JSON.parse() it
this is what I get when I try
TypeError: Cannot convert object to primitive value
and all that Im doing on data on backend is this
console.log(JSON.parse(req.body)); // in the first example it was the same thing but only without JSON.parse()
Anyone have an idea why this is happening here?
Even if you have a suggestion on what I could try feel free to post here, otherwise I'm gonna have to write a function for parsing these badass inputs lol.
All the parsing/stringifying is handled by Node, so don't worry about it, just pass your object as it is, and you'll be able to access its properties in req.body.
client :
$.ajax({
type: 'POST',
url: url,
data: postBody,
dataType: 'json',
success: function(data) {
console.log(data);
},
error: function(err) {
console.log(err);
}
});
server :
console.log(req.body); //should give you [Object object]
you can then send it right back with: res.status(200).json(req.body));
and read it in your ajax callback :
success: function(data) {
console.log(data);
},
i have a kendo ui grid with asp web api as backend. When i call the create method in the kendo ui, it's called the following method in web api
public IHttpActionResult PostProduct(ProductDTO product)
{
...
...
return StatusCode(HttpStatusCode.NoContent);
}
Now if i try to edit the item in the Kendo Ui Grid is called again the create method instead of the update method.
If i reload the page (so is called the read method of kendo ui grid), the update method works.
What's the problem? I have the following schema:
schema: {
model: {
id: "Id",
fields: {
Id: { editable: false, type: "number" },
Name: { validation: { required: true } },
Description: { editable: true },
Price: { editable: true },
Active: { type: "boolean" },
}
}
}
I have the following transport (omitted some code)
$scope.tabellaProdotto = new kendo.data.DataSource({
transport: {
read: {
url: function () {
return "api/Prodotti/GetProdottoPerTipoProdotto/" + productTypeMainSelected;
},
dataType: "json"
},
create: {
url: "api/Prodotti/PostProdotto",
dataType: "json",
data: function (prodottoTmp) {
...
},
type: "POST"
},
update: {
url: function (prodotto) {
return "api/Prodotti/PutProdotto" + prodotto.Id
},
data: function (prodottoTmp) {
...
},
type: "PUT",
dataType: "json"
UPDATE: the problem seems be the return of the web api action method:
return CreatedAtRoute("DefaultApi", new { id = p.Id }, p);
Now works but the p object size dimension is very high: i must return the entire object?
This sounds like the Grid is not getting the Json back in the right format.
Be sure to use the KendoMVC DataSourceRequest Object to return data in the right format.
Here is an example:
public ActionResult Update([DataSourceRequest] DataSourceRequest request, MyViewModel data)
{
var result = UpdateBackend(data);
return Json(result.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
Just want to know why push method of the javascript inserts "index"
var agendaBatch=[];
for(var i=0; i<agendas.length; i++) {
var agenda = {
MeetingId: meetingId,
Title: agendas[i].title,
Description: agendas[i].description,
Remarks: "",
};
agendaBatch.push(agenda);
}
console.log(kendo.stringify(agendaBatch));
dataSourceAgenda.add(agendaBatch);
dataSourceAgenda.sync();
output:
{"0":{"Title":"Agenda title","Description":"Agenda details","Remarks":""},
"1":{"Title":"Agenda title","Description":"Agenda details","Remarks":""}}
what I expect is this output to match my Web API parameter requirement
[{"Title":"Agenda title","Description":"Agenda details","Remarks":""},
{"Title":"Agenda title","Description":"Agenda details","Remarks":""}]
Any suggestions how can I do this?....
UPDATE: just found out a moment ago, I'm using kendo ui datasource, I fixed the problem when I removed the Id on the schema
var dataSourceAgenda = new kendo.data.DataSource({
transport: {
type: "odata",
create: {
type: "POST",
url: API_URL + "/agendas",
contentType: "application/json; charset=utf-8",
dataType: 'json'
},
parameterMap: function (options, operation) {
if (operation !== "read" && options) {
return kendo.stringify(options);
}
}
},
schema: {
model: {
id: "Id", //I get my desired output if this is removed
fields: {
MeetingId: { type: "number" },
Title: { type: "string" },
Description: { type: "string" },
Remarks: { type: "string" },
}
},
}
});
HOWEVER I need to the Id parameter in other functions, is there anyway I can do this without removing the Id in kendo datasource.
Changed the Question title!
According the documentation of Kendo UI DataSource (here), add method accepts an Object not an array of Object.
In addition, you use as id a field called Id that is not among the fields of your model.
Try doing the following:
var dataSourceAgenda = new kendo.data.DataSource({
transport: {
create : function (op) {
...
},
parameterMap: function (options, operation) {
if (operation !== "read" && options) {
return kendo.stringify(options.models);
}
}
},
batch : true,
schema : {
model: {
id : "Id", //I get my desired output if this is removed
fields: {
Id : { type: "number" },
MeetingId : { type: "number" },
Title : { type: "string" },
Description: { type: "string" },
Remarks : { type: "string" }
}
}
}
});
I.e.:
Set batch to true for being able to send multiple requests at a time when you invoke sync.
Define Id in the schema.model.fields definition.
Do the stringify of options.models.
As agendaBatch is obviously an array, I assume that kendo.stringify is not serializing it properly. You could go with JSON.stringify.
Note that this is not implemented by older browsers. If you need to support them, you could include the script by Douglas Crockford:
https://github.com/douglascrockford/JSON-js/blob/master/json2.js
EDIT
Now that you changed your question - I am not really familiar with kendo ui, so this really is just a wild guess in an attempt to help you with your updated problem.
It looks like you have access to the data in the beforeSend function. You could try to manipulate it for your needs, like this maybe:
beforeSend: function (xhr, s) {
var arrayData = [];
for (var id in s.data) {
arrayData.push(s.data[id]);
}
s.data = arrayData;
}