axios API call after file is ready - javascript

I'm trying to upload file to a server.
const uploadFileCall = (file) => {
const formData = new FileReader();
formData.readAsArrayBuffer(file);
return axios({
method: 'post',
url: `file/${file.name}`,
data: {
fileContent: formData.result,
},
});
};
How to wait with a call api till formData will be ready? Should I use callback or create a new promise to wait for a prepared file?

You can handle it with the FileReader API using the onload() method
Look at this link the documentation is pretty clear. You should be able to adapt your code with this example.

You have to make call back from your axios call to action.
//post Call
function postCall(url, data, callbackProgressUpload = null) {
Date.prototype.toJSON = function () {
return moment(this).format();
}
const requestOptions = {
method: 'POST',
headers: authHeader(),
body: JSON.stringify(data),
onUploadProgress: function (progressEvent) {
// var percentCompleted = Math.round( (progressEvent.loaded * 100) / progressEvent.total );
if (callbackProgressUpload)
callbackProgressUpload(progressEvent);
}
};
return axios.post(baseUrl + url, data, requestOptions).then(
response => {
return response;
}
).catch(function (error) {
return Promise.reject(error);
});
}
*************************************
//service Call
function storeFiles(imagesData, orderId, folder, callback) {
return SC.postCall(`${url}`, fileData, (response) => {
callback(response);
}
);
}
********************************
//action
export function storeFiles(imagesData, orderId, folder) {
return dispatch => {
return orderService.storeFiles(imagesData, orderId, folder, (progress) => {
var percentCompleted = Math.round((progress.loaded * 100) / progress.total);
console.log('percentCompleted', percentCompleted)
}).then(
response => {
return response;
},
error => {
return Promise.reject(error);
}
)
}
}

Related

Service call is not going in react-native. Getting warning like "Possible unhandled Promise Rejection, Reference error: response is not defined"

I am new to react native and making service call for the first time. My problem is service call is not going and getting warning like
Possible unhandled Promise Rejection, Reference error: response is not defined.
I am trying to hit loginUser function.
Api.js
const BASE_URL = "http://localhost:8200";
export const api = async (url, method, body = null, headers = {}) => {
try {
const endPoint = BASE_URL.concat(url);
const reqBody = body ? JSON.stringify(body) : null;
const fetchParams = {method, headers};
if((method === "POST" || method === "PUT") && !reqBody) {
throw new Error("Request body required");
}
if(reqBody) {
console.log("ReQBody--->"+reqBody);
fetchParams.headers["Content-type"] = "application/json";
fetchParams.body = reqBody;
}
const fetchPromise = await fetch(endPoint, fetchParams);
const timeOutPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject("Request Timeout");
}, 3000);
});
const response = await Promise.race([fetchPromise, timeOutPromise]);
return response;
} catch (e) {
return e;
}
}
export const fetchApi = async (url, method, body, statusCode, token = null, loader = false) => {
console.log("In FetchAPi Function");
try {
const headers = {}
const result = {
token: null,
success: false,
responseBody: null
};
if(token) {
headers["securityKey"] = token;
}
const response = await api(url, method, body, headers);
console.log("fetchApi-->>"+response);
if(response.status === statusCode) {
result.success = true;
let responseBody;
const responseText = await response.text();
try {
responseBody = JSON.parse(responseText);
} catch (e) {
responseBody = responseText;
}
result.responseBody = responseBody;
return result;
}
let errorBody;
const errorText = await response.text();
try {
errorBody = JSON.parse(errorText);
} catch (e) {
errorBody = errorText;
}
result.responseBody = errorBody;
console.log("FetchApi(Result)--->>"+result);
throw result;
} catch (error) {
return error;
}
}
auth.actions.js
export const loginUser = (payload) => {
console.log("In LoginUser function2");
return async (dispatch) => {
<-----**I am not able to enter into this block**------>
try {
dispatch({
type: "LOGIN_USER_LOADING"
});
console.log("In LoginUser function3");
const response = await fetchApi("/login", "POST", payload, 200);
if(response.success) {
dispatch({
type: "LOGIN_USER_SUCCESS",
});
dispatch({
type: "AUTH_USER_SUCCESS",
token: response.token
});
dispatch({
type: "GET_USER_SUCCESS",
payload: response.responseBody
});
return response;
} else {
throw response;
}
} catch (error) {
dispatch({
type: "LOGIN_USER_FAIL",
payload: error.responseBody
});
return error;
}
}
}
In console log, I can't see anything in network tab. In the android emulator, the mentioned warning has come.
My console tab
I see that your BASE_URL is served using an http endpoint. You can only make requests to https endpoints from react native projects. A possible workaround is to use ngrok. Just download it and run ./ngrok http 8200 since your port number is 8200. It will expose an HTTPS endpoint and replace your BASE_URL with that link and try fetching the data again.
I use the following code to make API calls. See if you can integrate it in your code. it is quite simple:
In a class called FetchService:
class FetchService {
adminAuth(cb, data) {
console.log('here in the fetch service');
return fetch(
baseURL + "login",
{
method: "POST",
headers: {
Accept: "application/json",
},
body: data
}
)
.then((response) => response.json())
.then(responsej => {
cb(null, responsej);
})
.catch(error => {
cb(error, null);
});
}
}
export default FetchService;
Then call it from your component using:
import FetchService from './FetchService';
const fetcher = new FetchService;
export default class LoginScreen extends React.Component {
fetchData() {
const data = new FormData();
data.append('username',this.state.username);
data.append('password',this.state.password);
fetcher.wastereport((err, responsej) => {
if(err) {
//handle error here
} else {
//handle response here
}
}, data);
}
}

Redux Thunk - Change state before REST request

I am quite new to Redux Thunk and have an issue that I want to update a contract with a 'FileList' (file appendix), but if I use JSON.stringify the file will have a 0 value. If I convert the file to Base64 this problem is solved, but the PUT request is performed before the file is converted.
I searched a lot about Redux Thunk and think it might be some issue with Dispatch, I tried quite a lot and didn't become much wiser. Most of the things that I tried returned: "Actions must be plain objects. Use custom middleware for async actions."
Would appreciate some help or some search suggestions..
ps. contract.answers[0].answer[0] is the file. This acquires some refactoring, but first it needs to work.
const toBase64 = (file) => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
export function updateContract(contract) {
const base64File = toBase64(contract.answers[0].answer[0]);
base64File.then((value) => {
contract.answers[0].answer[0] = value; //Set file as base64
});
return {
type: SAVE,
fetchConfig: {
uri: contract._links.self,
method: 'PUT',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(contract), // Does not handle files
failureHandler(error) {
const {
details,
status,
} = error;
// If the contract was invalid, throw form errors:
if (status.code === 400 && details) {
// Map the question ids to fields:
throw new SubmissionError(Object.keys(details).reduce(
(acc, questionId) => {
acc[`question${questionId}`] = details[questionId];
return acc;
},
{},
));
}
return {
type: SAVE_FAILURE,
error,
};
},
successHandler(json) {
return {
type: SAVE_SUCCESS,
data: json,
};
},
},
};
}
Kind regards,
Gust de Backer
This happen because toBase64 return a Promise and itself is async, so in your case is necessary encapsule inside a new then.
export function updateContract(contract) {
const base64File = toBase64(contract.answers[0].answer[0]);
base64File.then((value) => {
contract.answers[0].answer[0] = value; //Set file as base64
});
return (dispatch) => {
base64File.then(() => dispatch({
type: SAVE,
fetchConfig: {
uri: contract._links.self,
method: 'PUT',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(contract), // Does not handle files
failureHandler(error) {
const {
details,
status,
} = error;
// If the contract was invalid, throw form errors:
if (status.code === 400 && details) {
// Map the question ids to fields:
throw new SubmissionError(Object.keys(details).reduce(
(acc, questionId) => {
acc[`question${questionId}`] = details[questionId];
return acc;
}, {},
));
}
return {
type: SAVE_FAILURE,
error,
};
},
successHandler(json) {
return {
type: SAVE_SUCCESS,
data: json,
};
},
},
}))
};
}
Yes, the redux accept a function as return, that function receive a dispatch on params, you can use it to dispatch the request after convert is ready :)

Return promise after multiple calls with the same instance of an Ajax method

I'm using Ajax with JQuery to fetch data from an API that only returns 100 records at a time. If my query gives a result with more than 100 records, the API will include an "offset" parameter in the response. I have to use this offset parameter in a new API call to get the next 100 records. The API will include a new offset parameter if there's even more records to fetch. And so on until all records are fetched.
As you can see I've solved this by having the function call itself until the "offset" parameter is no longer included. I.e. until there are no more records to fetch.
Because of this behavior of the API, I cannot use the Ajax method's own .done-function, since it would be executed multiple times (for each iteration of the Ajax method).
How can adjust the function below to return a promise when all Ajax calls have been done?
function getContracts(offset) {
var data = {};
if (offset !== undefined) {
data["offset"] = offset;
}
$.ajax({
url: url,
headers: {
Authorization: apiKey
},
data: data,
success: function(result){
$.each(result.records, function() {
contracts.push(this);
});
if (result.hasOwnProperty("offset")) {
getContracts(result.offset);
}
}
});
}
Real and full code as requested:
var objectContracts = [];
var landContracts = [];
var locations = [];
var customers = [];
var landOwners = [];
var frameworkAgreements = [];
function getObjectContracts(offset) {
return new Promise((resolve, reject) => {
var data = {};
data["view"] = 'Alla Objektsavtal';
if (offset !== undefined) {
data["offset"] = offset;
}
$.ajax({
url: url + "Objektsavtal",
headers: {
Authorization: apiKey
},
data: data,
success: function(result){
$.each(result.records, function() {
objectContracts.push(this);
});
if (result.hasOwnProperty("offset")) {
getObjectContracts(result.offset);
} else {
resolve();
}
}
});
});
}
function getLandContracts(offset) {
return new Promise((resolve, reject) => {
var data = {};
data["view"] = 'Alla Markavtal';
if (offset !== undefined) {
data["offset"] = offset;
}
$.ajax({
url: url + "Markavtal",
headers: {
Authorization: apiKey
},
data: data,
success: function(result){
$.each(result.records, function() {
landContracts.push(this);
});
if (result.hasOwnProperty("offset")) {
getLandContracts(result.offset);
} else {
resolve();
}
}
});
});
}
function getLocations(offset) {
return new Promise((resolve, reject) => {
var data = {};
data["view"] = 'Alla Uppställningsplatser';
if (offset !== undefined) {
data["offset"] = offset;
}
$.ajax({
url: url + "Uppställningsplatser",
headers: {
Authorization: apiKey
},
data: data,
success: function(result){
$.each(result.records, function() {
locations.push(this);
});
if (result.hasOwnProperty("offset")) {
getLocations(result.offset);
} else {
resolve();
}
}
});
});
}
function getCustomers(offset) {
return new Promise((resolve, reject) => {
var data = {};
data["view"] = 'Alla Kunder';
if (offset !== undefined) {
data["offset"] = offset;
}
$.ajax({
url: url + "Kunder",
headers: {
Authorization: apiKey
},
data: data,
success: function(result){
$.each(result.records, function() {
customers.push(this);
});
if (result.hasOwnProperty("offset")) {
getCustomers(result.offset);
} else {
resolve();
}
}
});
});
}
function getLandOwners(offset) {
return new Promise((resolve, reject) => {
var data = {};
data["view"] = 'Alla Markägare';
if (offset !== undefined) {
data["offset"] = offset;
}
$.ajax({
url: url + "Markägare",
headers: {
Authorization: apiKey
},
data: data,
success: function(result){
$.each(result.records, function() {
landOwners.push(this);
});
if (result.hasOwnProperty("offset")) {
getLandOwners(result.offset);
} else {
resolve();
}
}
});
});
}
function getFrameworkAgreements(offset) {
return new Promise((resolve, reject) => {
var data = {};
data["view"] = 'Alla Ramavtal';
if (offset !== undefined) {
data["offset"] = offset;
}
$.ajax({
url: url + "Ramavtal",
headers: {
Authorization: apiKey
},
data: data,
success: function(result){
$.each(result.records, function() {
frameworkAgreements.push(this);
});
if (result.hasOwnProperty("offset")) {
getFrameworkAgreements(result.offset);
} else {
resolve();
}
}
});
});
}
If I've understood your question perfectly, you want to resolve a Promise if there is no offset in the response from your Ajax request.
I haven't tested this code but you can do something like this:
function getContracts(offset) {
return new Promise((resolve, reject) => {
var data = {};
if (offset !== undefined) {
data['offset'] = offset;
}
$.ajax({
url: url,
headers: {
Authorization: apiKey,
},
data: data,
success: function(result) {
$.each(result.records, function() {
contracts.push(this);
});
if (result.hasOwnProperty('offset')) {
getContracts(result.offset);
} else {
// I guess this is what you want
// If there is no offset property => resolve the promise
resolve('Your result goes here');
}
},
});
});
}
See the else block.
You can pass your final result (whatever you want to achieve after the completion of your task) inside the resolve. For example, you can create an array and append your result to that and at the end, you can pass that array inside resolve.
You can resolve this using .then() or async/await
async () => {
const result = await getContracts(offset);
};
or
getContracts(offset).then(result => { console.log(result) });
If you see some Unhandled Promise Rejection warning/error, you can always use try/catch block with async/await and .catch after .then.
EDIT:
First, you're not passing anything inside resolve. Whatever you pass inside the resolve will be reflected in .then(result).
Second, you have global variables and storing all your data inside them. So now you don't need to pass them inside the resolve but this is not a good approach because any function or the code outside can modify it. So I'll give you one example.
function getObjectContracts(offset) {
return new Promise((resolve, reject) => {
var data = {};
const objectContracts = [];
data['view'] = 'Alla Objektsavtal';
if (offset !== undefined) {
data['offset'] = offset;
}
$.ajax({
url: url + 'Objektsavtal',
headers: {
Authorization: apiKey,
},
data: data,
success: function(result) {
$.each(result.records, function() {
objectContracts.push(this);
});
if (result.hasOwnProperty('offset')) {
getObjectContracts(result.offset);
} else {
resolve(objectContracts);
}
},
});
});
}
Now, the other question is, how to resolve all these promises at once.
const finalFunction = async () => {
const [result1, result2, result3] = await Promise.all([
getObjectContracts(offset1),
getLandContracts(offset2),
getLocations(offset3),
]);
console.log(result1, result2, result3);
};
finalFunction();

Axios: config.method.toLowerCase is not a function

I'm making a GET request with Axios in a React-Redux project, and I get the following error:
TypeError: "config.method.toLowerCase is not a function"
request Axios.js:43
wrap bind.js:11
apiCall api.js:32
apiCall api.js:29
... ...
api.js is a file from my own project. bind.js and Axios.js is from the Axios library. This is my api function:
export function apiCall(method, path, data){
let url = backendDomain + path
let config = {
method: [method],
url: [url],
data : [data],
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
}
}
return new Promise((resolve, reject)=>{
return axios(config).then(res=> {
return resolve(res.data)
}).catch(err => {
return reject(err.response);
})
})
The function that makes use of apiCall() is this function:
export function authUser(url, userData, method){
return (dispatch) => {
return new Promise((resolve, reject)=>{
return apiCall(method, "/"+`${url}`, userData)
.then((data) => {
...
resolve();
})
.catch(err=>{
...
reject();
})
})
}
}
Do you think there's something wrong with my code or is there something wrong with the library? When I use authUser to dispatch my action (for Redux State), I double checked that "method" is a String, I console.logged typeof method in api.js, and it returned string.
Edit:
I tried calling toString() to the method parameter passed into apiCall(), but it didn't work:
let reMethod = method.toString();
const config = {
method: [reMethod],
url: [url],
data : [data],
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
}
}
As mentioned in my comment, you're providing an array when axios expects a string:
const config = {
method: reMethod,
url: url,
data : data,
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
}
}

Amazon S3 Remote File Upload with Axios

I am trying to write a function that would:
Take a remote URL as a parameter,
Get the file using axios
Upload the stream to amazon s3
And finally, return the uploaded url
I found help here on stackoverflow. So far, I have this:
/*
* Method to pipe the stream
*/
const uploadFromStream = (file_name, content_type) => {
const pass = new stream.PassThrough();
const obj_key = generateObjKey(file_name);
const params = { Bucket: config.bucket, ACL: config.acl, Key: obj_key, ContentType: content_type, Body: pass };
s3.upload(params, function(err, data) {
if(!err){
return data.Location;
} else {
console.log(err, data);
}
});
return pass;
}
/*
* Method to upload remote file to s3
*/
const uploadRemoteFileToS3 = async (remoteAddr) => {
axios({
method: 'get',
url: remoteAddr,
responseType: 'stream'
}).then( (response) => {
if(response.status===200){
const file_name = remoteAddr.substring(remoteAddr.lastIndexOf('/')+1);
const content_type = response.headers['content-type'];
response.data.pipe(uploadFromStream(file_name, content_type));
}
});
}
But uploadRemoteFileToS3 does not return anything (because it's a asynchronous function). How can I get the uploaded url?
UPDATE
I have further improved upon the code and wrote a class. Here is what I have now:
const config = require('../config.json');
const stream = require('stream');
const axios = require('axios');
const AWS = require('aws-sdk');
class S3RemoteUploader {
constructor(remoteAddr){
this.remoteAddr = remoteAddr;
this.stream = stream;
this.axios = axios;
this.config = config;
this.AWS = AWS;
this.AWS.config.update({
accessKeyId: this.config.api_key,
secretAccessKey: this.config.api_secret
});
this.spacesEndpoint = new this.AWS.Endpoint(this.config.endpoint);
this.s3 = new this.AWS.S3({endpoint: this.spacesEndpoint});
this.file_name = this.remoteAddr.substring(this.remoteAddr.lastIndexOf('/')+1);
this.obj_key = this.config.subfolder+'/'+this.file_name;
this.content_type = 'application/octet-stream';
this.uploadStream();
}
uploadStream(){
const pass = new this.stream.PassThrough();
this.promise = this.s3.upload({
Bucket: this.config.bucket,
Key: this.obj_key,
ACL: this.config.acl,
Body: pass,
ContentType: this.content_type
}).promise();
return pass;
}
initiateAxiosCall() {
axios({
method: 'get',
url: this.remoteAddr,
responseType: 'stream'
}).then( (response) => {
if(response.status===200){
this.content_type = response.headers['content-type'];
response.data.pipe(this.uploadStream());
}
});
}
dispatch() {
this.initiateAxiosCall();
}
async finish(){
//console.log(this.promise); /* return Promise { Pending } */
return this.promise.then( (r) => {
console.log(r.Location);
return r.Location;
}).catch( (e)=>{
console.log(e);
});
}
run() {
this.dispatch();
this.finish();
}
}
But still have no clue how to catch the result when the promise is resolved. So far, I tried these:
testUpload = new S3RemoteUploader('https://avatars2.githubusercontent.com/u/41177');
testUpload.run();
//console.log(testUpload.promise); /* Returns Promise { Pending } */
testUpload.promise.then(r => console.log); // does nothing
But none of the above works. I have a feeling I am missing something very subtle. Any clue, anyone?
After an upload you can call the getsignedurl function in s3 sdk to get the url where you can also specify the expiry of the url as well. You need to pass the key for that function. Now travelling will update with example later.
To generate a simple pre-signed URL that allows any user to view the
contents of a private object in a bucket you own, you can use the
following call to getSignedUrl():
var s3 = new AWS.S3();
var params = {Bucket: 'myBucket', Key: 'myKey'};
s3.getSignedUrl('getObject', params, function (err, url) {
console.log("The URL is", url);
});
Official documentation link
http://docs.amazonaws.cn/en_us/AWSJavaScriptSDK/guide/node-examples.html
Code must be something like this
function uploadFileToS3AndGenerateUrl(cb) {
const pass = new stream.PassThrough();//I have generated streams from file. Using this since this is what you have used. Must be a valid one.
var params = {
Bucket: "your-bucket", // required
Key: key , // required
Body: pass,
ContentType: 'your content type',
};
s3.upload(params, function(s3Err, data) {
if (s3Err) {
cb(s3Err)
}
console.log(`File uploaded successfully at ${data.Location}`)
const params = {
Bucket: 'your-bucket',
Key: data.key,
Expires: 180
};
s3.getSignedUrl('getObject', params, (urlErr, urlData) => {
if (urlErr) {
console.log('There was an error getting your files: ' + urlErr);
cb(urlErr);
} else {
console.log(`url: ${urlData}`);
cb(null, urlData);
}
})
})
}
Please check i have update your code might its help you.
/*
* Method to upload remote file to s3
*/
const uploadRemoteFileToS3 = async (remoteAddr) => {
const response = await axios({
method: 'get',
url: remoteAddr,
responseType: 'stream'
})
if(response.status===200){
const file_name = remoteAddr.substring(remoteAddr.lastIndexOf('/')+1);
const content_type = response.headers['content-type'];
response.data.pipe(uploadFromStream(file_name, content_type));
}
return new Promise((resolve, reject) => {
response.data.on('end', (response) => {
console.log(response)
resolve(response)
})
response.data.on('error', () => {
console.log(response);
reject(response)
})
})
};
*
* Method to pipe the stream
*/
const uploadFromStream = (file_name, content_type) => {
return new Promise((resolve, reject) => {
const pass = new stream.PassThrough();
const obj_key = generateObjKey(file_name);
const params = { Bucket: config.bucket, ACL: config.acl, Key: obj_key, ContentType: content_type, Body: pass };
s3.upload(params, function(err, data) {
if(!err){
console.log(data)
return resolve(data.Location);
} else {
console.log(err)
return reject(err);
}
});
});
}
//call uploadRemoteFileToS3
uploadRemoteFileToS3(remoteAddr)
.then((finalResponse) => {
console.log(finalResponse)
})
.catch((err) => {
console.log(err);
});

Categories

Resources