Upload image using ionic, angular, and django+restframework - javascript

I'm developping an app, in which I want to be able to upload a photo to the backend (restframework + django).
For the backend side, I followed this tutorial.
When I use :
curl --verbose --header "Authorization: token {TOKEN}" --header "Accept: application/json; indent=4" --request POST --form photo=#/home/.../uyuni.jpg {DOMAIN}/api/users/1/photo/
This works perfectly, the file is really created, and I can retrieve it afterwards, using ng-src.
Then, I want to wire frontend to upload photo.
Here I tried lots of ideas from forums or tutos like this one
I have two different issues.
If I copy the last tuto, I end up with a 400 Bad Request:
Erreur d'analyse XML : aucun élément trouvé Emplacement : moz-nullprincipal:{1b4583fc...} Numéro de ligne 1, Colonne 1 :
I don't know if it comes from backend or mozilla. I read about CORS issue, but this shouldn't be a problem, as I use ionic proxy.
If I wire with cordova-plugin-camera, I have an URL (file:///home/.../example.jpg) returned, I don't know how to wire this to the FormData to use for the POST to the backend:
var updateUserPhoto = function (userId, photoURI) {
if (!photoURI) {return}
var fd = new FormData();
fd.append('photo', photoURI);
return $http.post(API_URL + "users/"+userId+"/photo/", fd, {
transformRequest: angular.identity,
headers: {AUTHORIZATION: "Token "+window.localStorage['apiToken'], 'CONTENT-TYPE': undefined}
});
};
I tried to append an object in fd, like {path: photoURI, name: 'blablabla'}
In this issue, I end up with a Django error:
AttributeError at /api/users/1/photo/ 'str' object has no attribute 'name'
I'm probably not that far, so I'd rather not use external snippets.

I had very similar issues, and then some. This Media plugin from the Ionic marketplace resolved the issue by providing both base64 data as well as uri for me to use.

I finally solved my issue. Ouf!
I used this on backend side:
import base64
from django.db.models import ImageField
from django.db.models.fields.files import ImageFieldFile
from django.core.files.uploadedfile import InMemoryUploadedFile
def upload_to(instance, filename):
return 'profiles/' + filename
def wrap(content):
class File(InMemoryUploadedFile):
def read(self):
return base64.b64decode(super().read())
return File(content.file, content.field_name, content.name, content.content_type, content.size, content.charset)
class Base64ImageFieldFile(ImageFieldFile):
def save(self, name, content, save=True):
super().save(name, wrap(content), save=True)
class Base64ImageField(ImageField):
attr_class = Base64ImageFieldFile
And on frontend side:
To configure my resource:
var userActions = getActions(User);
var updatePhotoHeader = angular.copy(auth_header);
updatePhotoHeader['CONTENT-TYPE'] = undefined;
userActions['updatePhoto'] = {
method: "POST",
url: API_URL + "users/:userId/photo/",
params: {userId: "#userId"},
headers: updatePhotoHeader,
transformRequest: function (data) {
if (!data) {return}
var fd = new FormData();
fd.append('photo', new Blob([data.photo], {type: 'image\/jpeg'}));
return fd
}
};
with this options for the camera (from cordova plugin):
var cameraOptions = {
quality: 75,
destinationType: 0,
targetWidth: 750,
targetHeight: 500,
saveToPhotoAlbum: false,
cameraDirection: 1,
allowEdit: true,
encodingType: 0 // 0=JPG 1=PNG
};
PS: This solution might not be the best. Still hope to find a better one. But I will go on with this for now.

Related

Call function that returns href property when user clicks button Vue.js

So, first time posting, I usually just find the answer I need by looking through similar questions but this time I'm stumped.
First off, I'm self-taught and about on par with an entry-level developer at absolute best (for reference my highest score on CodeSignal for javascript is 725).
Here is my problem:
I'm working on an SSG eCommerce website using the Nuxt.js framework. The products are digital and so they need to be fulfilled by providing a time-limited download link when a customer makes a purchase. I have the product files stored in a private amazon s3 bucket. I also have a Netlify Serverless Function that when called with a GET request, generates and returns a pre-signed URL for the file (at the moment there is only one product but ideally it should generate pre-signed URLs based on a filename sent as a JSON event body key string since more products are planned in the near future, But I can figure that out once the whole thing is working).
The website is set up to generate dynamic routes based on the user's order number so they can view their previous orders(/pages/account/orders/_id.vue). I have placed a download button, nested in an element on this page so that each order has a button to download the files. The idea is that button press calls a function I defined in the methods object. The function makes an XMLHttpRequest to the endpoint URL of the netlify function. Netlify function returns pre-signed URL to function which returns pre-signed URL to the href property so that file can be downloaded by the user.
But no matter what I try, it fails to download the file. When the page loads it successfully calls the Netlify function and I get a response code 200 but the href property remains blank. Am I going about this the wrong way? There is clearly something I'm not understanding correctly, Any input is greatly appreciated.
Here is my code....
The download button:
<a
:download=<<MY_PRODUCT_NAME>>
:href="getmyurl()"
>
<BaseButton
v-if="order.status === 'complete'"
fit="auto"
appearance="light"
label="Download"
/>
</a>
function that button calls:
methods: {
getmyurl() {
let myurl = "";
const funcurl = <<MY_NETLIFY_FUNCTION_URL>>;
let xhr = new XMLHttpRequest();
xhr.open('GET', funcurl);
xhr.send();
xhr.onload = function() {
if (xhr.status != 200) {
alert(`Error ${xhr.status}: ${xhr.statusText}`);
} else {
myurl = xhr.response.Geturl
};
};
return myurl
},
Netlify function:
require( "dotenv" ).config();
const AWS = require('aws-sdk');
let s3 = new AWS.S3({
accessKeyId: process.env.MY_AWS_ACCESS_KEY,
secretAccessKey: process.env.MY_AWS_SECRET_KEY,
region: process.env.MY_AWS_REGION,
signatureVersion: 'v4',
});
exports.handler = function( event, context, callback ) {
var headers = {
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Headers": "Content-Type"
};
if ( event.httpMethod === "OPTIONS" ) {
callback(
null,
{
statusCode: 200,
headers: headers,
body: JSON.stringify( "OK" )
}
);
return;
}
try {
var resourceKey = process.env.MY_FILE_NAME
var getParams = {
Bucket: process.env.MY_S3_BUCKET,
Key: resourceKey,
Expires: ( 60 * 60 ),
ResponseCacheControl: "max-age=604800"
};
var getUrl = s3.getSignedUrl( "getObject", getParams );
var response = {
statusCode: 200,
headers: headers,
body: JSON.stringify({
getUrl: getUrl
})
};
} catch ( error ) {
console.error( error );
var response = {
statusCode: 400,
headers: headers,
body: JSON.stringify({
message: "Request could not be processed."
})
};
}
callback( null, response );
}

Getting Status: "Not Acceptable" while trying to update a variant price form Rest API Admin Shopify in Airtable Scripts

this is my first question here in SO, so thanks to any one that help me with this problem.
Basically im trying to update in a PUT request a variant price. I'm using Airtbale scripting to run a javascript, and on this script i fetch the info to call the api and make the update.
var raw = JSON.stringify({
"variant": {
"price": "1011",
"compare_at_price": "1011"
}
});
console.log(raw);
var myHeaders = new Headers();
myHeaders.append("X-Shopify-Access-Token", api_token);
myHeaders.append("Content-Type", "application/json");
const params = {
method:'PUT',
headers:myHeaders,
body: raw,
};
const url =
`https://tryp-mexico.myshopify.com/admin/admin/api/2022-04/variants/42242378334462.json`
const response = await fetch(url,params);
console.log(response);
When i try tu run this update on Postman, it works well. But here im getting:
{type: "basic", url: "https://tryp-mexico.myshopify.com/admin/admin/api/2022-04/variants/42242378334462.json", status: 406, statusText: "Not Acceptable", ok: false…}
¡Thanks a lot for your help!
Your URL has "admin" twice.
const url =
`https://tryp-mexico.myshopify.com/admin/admin/api/2022-04/variants/42242378334462.json`
Are you using a variable for the Shop URI, that possibly already has /admin in it?
This is the correct URI format for variants:
https://tryp-mexico.myshopify.com/admin/api/2022-04/variants/42242378334462.json

Error opening a Salesforce File after creating a content version using REST API

I am using typescript (angular 4) to post to the salesforce endpoint
https://yourInstance.salesforce.com/services/data/v41.0/sobjects/ContentVersion/
My http request looks like the following
Request Headers
Content-Type: multipart/form-data; boundary="1524931327799"
Authorization: Bearer <token>
Request Body
--1524931327799
Content-Disposition: form-data; name="entity_document";
Content-Type: application/json; charset=UTF-8
{
"PathOnClient" : "IMG_0400.jpg",
"Title": "IMG_0400.jpg"
}
--1524931327799
Content-Type: image/jpeg
Content-Disposition: form-data; name="VersionData"; filename="IMG_0400.jpg"
/9j/4AAQSkZJRgABAQAAAQABAAD/4QBsRXhpZgAASUkqAAgA <rest of base64 data>
--1524931327799--
After opening the image on the salesforce platform I get an error that the image may be damaged or use a file format that Preview doesn’t recognize. When I open the image using text edit there is the identical base64 data that is sent in the request. It seems the problem comes with salesforce not recognizing that the file is an image and does not decode the base64 data. All and any help is welcomed! Thank you.
I just tried it using developer workbench and request like this should work just fine. Try not to define 2 different content types in the request, rather put definition of file into the VersionData attribute of ContentVersion Object
{
"PathOnClient" : "IMG_0400.jpg",
"Title": "IMG_0400.jpg",
"VersionData" : "4AAQSkZJRgABAQAAAQABAAD/4QBsRXhpZgAASUkqAAgA"
}
I was never able to post to the /ContentVersion/ endpoint. After doing some research the simplest solution I found was to use jsforce https://jsforce.github.io/.
Solution using jsforce:
1. Import jsforce library in your index.html "https://cdnjs.cloudflare.com/ajax/libs/jsforce/1.7.0/jsforce.min.js"
2. Import jsforce at the top of your angular component
declare var jsforce:any;
Start a connection with jsforce
var conn = new jsforce.Connection({
loginUrl: "https://test.salesforce.com/",
clientId : "",
clientSecret : "",
redirectUri : ""
});
3. Login to SF and post to composite endpoint using requestPost
var username = "";
var password = "";
conn.login(username, password, function(err, userInfo) {
if (err) { return console.error(err); }
var path = '/services/data/v41.0';
return conn.requestPost( path + '/composite/', {
'allOrNone' : true,
'compositeRequest' : [
{
'method' : 'POST',
'url' : path + '/sobjects/ContentVersion/',
'referenceId' : 'newFile',
'body' : {
'Title' : fileToPost.name,
'PathOnClient' : fileToPost.name,
'VersionData' : base64FileData
}
}
]
})

Best way to send a file and additional params from Angular client and handle them on the server

I have a service with the method which gets three parameters which should be sent to the server.
setMainPhotoFor: function(file, petName, petId) {
...
}
I have the following solution:
Client side
services.js
setMainPhotoFor: function(file, pet) {
var baseServerApiUrl = configuration.ServerApi;
var data = new FormData();
data.append("image", file);
data.append("petName", pet.Name);
data.append("petId", pet.ID);
$http.post(baseServerApiUrl + '/pictures/main-picture/add', data, {
headers: { "Content-Type": undefined }
});
}
Server side
PicturesApiController
[HttpPost]
[Route("main-picture/add")]
public async Task<HttpResponseMessage> SetMainPicture()
{
if (!Request.Content.IsMimeMultipartContent())
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
MemoryStream mainPicture = new MemoryStream(await provider.Contents[0].ReadAsByteArrayAsync());
string petName = await provider.Contents[1].ReadAsStringAsync();
int petId;
if (!int.TryParse(await provider.Contents[2].ReadAsStringAsync(), out petId))
{
//...
}
//...
But in my opinion it doesn't look good. Can anybody suggest a right and more elegant solution for this task?
to send multipart form with angular add th options like this:
$http.post(baseServerApiUrl + '/pictures/main-picture/add', data, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
});
I have answered this before here with server side sample code.
Basically you should stream the content and put your dto in a header. I've tried many ways and this is the best way in my opinion.

Multipart/form-data form sending data with AngularJS

I need to post form data as below to URL which is a webapi and the method is post
var surveyData = {
Name: xyz,
SetReminder: 'false',
NoOfReminder: '0',
StartDate: 'xyz',
EndDate: 'xyz',
Language: 'eng-us',
Duration: '1',
ApplicationName: 'xyz',
SurveyTypeName: 'zxy'
};
i have written the below code which call the post method of wbeapi but the function is able to send the data but the post method is not able to read the data that is send using the angular js.
function(surveyData) {
return $http.post(URL , surveyData,{
headers: { 'Content-Type': 'multipart/form-data' }
});
Use :
var data = HttpContext.Current.Request.Form["surveyData"];
To recieve the json data from your multipart form.
If your multipart form contail files, then to get these files use :
System.Web.HttpFileCollection postedfiles = System.Web.HttpContext.Current.Request.Files;
Also keep in mind that do not use any parameter in your controller action method.
Make sure you import these namespaces:
using System.Net.Http;
using System.Web;
using System.Web.Http;
EDIT :
modify your javascript code.
var data = new FormData();
data.append("surveyData", angular.toJson(surveyData));
If you want to add images/other, you can add those using the code
below.
//data.append("uploadedFile", $scope.files[0]);
So your code will be :
function(surveyData) {
return $http.post(URL , data,{
headers: { 'Content-Type': 'multipart/form-data' }
});
Now you will be able to receive the json data using
var data = HttpContext.Current.Request.Form["surveyData"];
and images/otherfiles(If you have) using
System.Web.HttpFileCollection postedfiles = System.Web.HttpContext.Current.Request.Files;
if web api is Multipart/form-data then you need to create key value pair in the below form so that multi part form data will be created.
var objFormData = new FormData();
for (var key in surveyData)
objFormData.append(key, surveyData[key]);
then you need to send the created multi part form data using the below code:
return $http.post(URL, objFormData, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
});

Categories