Prevent a client from waiting indefinitely for server response - javascript

I have a scenario where a file is uploaded in the web application and is sent to a back end server to upload in storage. Till a response is received from the server a loading spinner is shown. In case the server is down after receiving the request, no response is received by the client and the web page still shows that the file upload is still in progress. Is there a way to check if the server is not responding and then show an error message to the user ?
export const upload = async (file) => {
let result = null;
try {
const formData = new FormData();
formData.append("file", file, file.name);
result = await API.post(
"/fileupload",
formData,
{}
);
if (result.status === 200) {
return result.data;
} else {
return "Upload file failed.";
}
} catch (ex) {
console.log("parsing failed", ex);
}
};

You can do this with a timeout. Ideally the library you're using allows you to set a timeout, but if not you can manually create a timeout using Promise.race
function timeout(ms) {
return new Promise(reject => setTimeout(() => reject(new Error('timeout'), ms));
}
export const upload = async (file) => {
let result = null;
try {
const formData = new FormData();
formData.append("file", file, file.name);
result = await Promise.race([
timeout(30000), // 30 seconds
API.post(
"/fileupload",
formData,
{}
),
]);
if (result.status === 200) {
return result.data;
} else {
return "Upload file failed.";
}
} catch (ex) {
if (ex.message === 'timeout') {
// timeout handling
}
console.log("parsing failed", ex);
}
};
Note that this can be pretty fragile and ideally you would use a library to handle uploads.
Ideally the timeout handling should be added at the API.post module level

Related

How to delete zip file after sent response in express

I just want to delete zip folder after sent response so I am looking for any alternative solution
Here is my code / it is get request
exports.Download = async (req, res) => {
try {
var zp = new admz();
zp.addLocalFolder(`./download/${folder}`);
const file_after_download = 'downloaded.zip';
const data = zp.toBuffer();
res.set('Content-Type', 'application/octet-stream');
res.set('Content-Disposition', `attachment; filename=${file_after_download}`);
res.set('Content-Length', data.length);
return res.send(data);
// HERE I want execute this code
let dir = `./download/${folder}`;
if (fse.existsSync(dir)) {
fse.rmdirSync(dir, { recursive: true })
}
} catch (err) {
console.log(err)
return res.render('pages/404');
}
}
Update
If send code without return ( res.send(data);)
Im getting this error //Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client //
If I put return res.send(data); at the end of block , then downloaded zip file will be empty - because its deleted already
From the docs of Express, you can use res.download() function which has a callback parameters to be executed once download is done.
res.download(filePath, 'yourFileName', function(err) {
if (err) {
next(err)
} else {
console.log('Delete:', filePath);
}
})

JS client doesn't read response when server aborts file upload

We have an API (Spring Boot) for file uploads. If file exists it returns 409.
But js client doesn't read this response and fails with error "net::ERR_CONNECTION_ABORTED".
From wireshark dump I see that the backend sends the response and then closes the connection:
I think the problem is that js client doesn't read the response as soon as it available.
However, Postman is able to read the response correctly.
Is it possible to implement Postman's behavior in javascript?
I've tried to read response stream with Fetch API, but no luck.
Sample client code:
function uploadFile() {
try {
console.log("Start file upload");
const selectedFiles = document.getElementById('input').files;
if (selectedFiles.length == 0) {
alert("No file selected");
return;
}
const file = selectedFiles[0];
return fetch("http://localhost:9091/api/v1/storage/upload?fileId=/upload-test/test.mp4", {
method: 'PUT',
body: file,
headers: {
'Content-Type': 'application/octet-stream'
},
})
.then(response => {
console.log("Processing response");
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
return pump();
function pump() {
return reader.read().then(({ value, done }) => {
if (done) {
console.log("Finished stream reading");
return;
}
console.log(value);
return pump();
});
}
})
.catch((err) => console.error("error:", err));
} catch (error) {
console.log(error);
}
}

How to return response from async function correctly & push it into another object

I have an Angular 11.x app that performs a http request to a backend system that reads data from a video file (e.g mp4/mov) using FFMPEG, due to the processing it takes 10 seconds to complete this async request.
I've hard coded some of the values for greater clarity
// video-component.ts
let fileUrl = 'https://abc.s3.eu-west-2.amazonaws.com/video.mp4';
let fileSize = '56117299';
this.videoMetadata = this.videoService.getVideoMediaData(fileUrl, fileSize);
// if any errors found from the async response loop through them and push them into the following error which displays this on the frontend
/* I need to push the errors from the request above into this `errorMessages` variable
self.errorMessages['Instagram'].push({
"message": "Video must be between 3-60 seconds in duration",
});
*/
// video.service.ts (downloads the file & gets metadata using FFMPEG in the endpoint)
public getMetadata(file: string, size: string): Observable<any> {
let params = new HttpParams();
params = params.append('file', file);
params = params.append('size', size);
return this.http.get('post/media-check', { params })
.pipe(map(response => {
return response;
}));
}
public getVideoMediaData(file, size) {
return new Promise((resolve, reject) => {
this.getMetadata(file, size).subscribe(
data => {
resolve(data);
},
errorResponse => {
}
);
});
}
The post/media-check in the getMetadata function hits an PHP endpoint and returns the following response similar to the following.
{
"status":"200",
"data":{
"video":{
"container":"mov",
"bitrate":338,
"stream":0,
"codec":"h264",
"fps":3
}
},
"errors":["Video must be at least 25 frames per second (fps)"],
"responseType":"json",
"response":"success"
}
How do I get the errors array from the backend response from the async request push directly into the self.errorMessages variable?
First you need to make sure that your video-service is handling errors properly.
public getVideoMediaData(file, size) {
return new Promise((resolve, reject) => {
this.getMetadata(file, size).subscribe(
data => {
resolve(data);
},
errorResponse => {
// Reject the Promise and pass the error response in the rejection
reject(errorResponse);
}
);
});
}
Then in your video-component you can handle this scenario like this:
let fileUrl = 'https://abc.s3.eu-west-2.amazonaws.com/video.mp4';
let fileSize = '56117299';
try {
this.videoMetadata = await this.videoService.getVideoMediaData(fileUrl, fileSize);
// happy path - do something with this.videoMetadata
} catch(e) {
// unhappy path - e = errorResponse
const messages = errorResponse.errors.map(message => ({ message }));
self.errorMessages['Instagram'].push(...messages);
}

Retrieve Result from Hyperledger Fabric Transaction

I submitted a transaction to Hyperledger Fabric, but I'd like to get object created by this.
The object that I get from this is Undefined.
Obs: The transaction is successfully created in Hyperledger Fabric.
async submit(resource, method) {
try{
this.businessNetworkDefinition = await this.bizNetworkConnection.connect(cardname);
if (!this.businessNetworkDefinition) {
console.log("Error in network connection");
throw "Error in network connection";
}
let factory = this.businessNetworkDefinition.getFactory();
let transaction = factory.newTransaction(NS, method);
Object.assign(transaction, resource)
return await this.bizNetworkConnection.submitTransaction(transaction);
}catch(error){
console.log(error);
throw error;
}
}
Currently the submitTransaction function is not returning anything. It is a bug or working as intended.
To go into more detail: When you delve through the source code of the composer you will finally get to the following code in composer-connector-hlfv1.
invokeChainCode(securityContext, functionName, args, options) {
const method = 'invokeChainCode';
LOG.entry(method, securityContext, functionName, args, options);
if (!this.businessNetworkIdentifier) {
return Promise.reject(new Error('No business network has been specified for this connection'));
}
// Check that a valid security context has been specified.
HLFUtil.securityCheck(securityContext);
// Validate all the arguments.
if (!functionName) {
return Promise.reject(new Error('functionName not specified'));
} else if (!Array.isArray(args)) {
return Promise.reject(new Error('args not specified'));
}
try {
args.forEach((arg) => {
if (typeof arg !== 'string') {
throw new Error('invalid arg specified: ' + arg);
}
});
} catch(error) {
return Promise.reject(error);
}
let txId = this._validateTxId(options);
let eventHandler;
// initialize the channel if it hasn't been initialized already otherwise verification will fail.
LOG.debug(method, 'loading channel configuration');
return this._initializeChannel()
.then(() => {
// check the event hubs and reconnect if possible. Do it here as the connection attempts are asynchronous
this._checkEventhubs();
// Submit the transaction to the endorsers.
const request = {
chaincodeId: this.businessNetworkIdentifier,
txId: txId,
fcn: functionName,
args: args
};
return this.channel.sendTransactionProposal(request); // node sdk will target all peers on the channel that are endorsingPeer
})
.then((results) => {
// Validate the endorsement results.
LOG.debug(method, `Received ${results.length} result(s) from invoking the composer runtime chaincode`, results);
const proposalResponses = results[0];
let {validResponses} = this._validatePeerResponses(proposalResponses, true);
// Submit the endorsed transaction to the primary orderers.
const proposal = results[1];
const header = results[2];
// check that we have a Chaincode listener setup and ready.
this._checkCCListener();
eventHandler = HLFConnection.createTxEventHandler(this.eventHubs, txId.getTransactionID(), this.commitTimeout);
eventHandler.startListening();
return this.channel.sendTransaction({
proposalResponses: validResponses,
proposal: proposal,
header: header
});
})
.then((response) => {
// If the transaction was successful, wait for it to be committed.
LOG.debug(method, 'Received response from orderer', response);
if (response.status !== 'SUCCESS') {
eventHandler.cancelListening();
throw new Error(`Failed to send peer responses for transaction '${txId.getTransactionID()}' to orderer. Response status '${response.status}'`);
}
return eventHandler.waitForEvents();
})
.then(() => {
LOG.exit(method);
})
.catch((error) => {
const newError = new Error('Error trying invoke business network. ' + error);
LOG.error(method, newError);
throw newError;
});
}
As you can see at the end, all that is happening is waiting for Events and Log.exit which return nothing. So currently you have to get your transaction result in another way.
The only way I could get something from my chaincode is through events. There's native interface that might be able to query for transaction data or something like this, but i haven't looked into it yet.

Read a JSON from AWS S3 Using JavaScript

I have this link to an AWS S3 bucket, which automatically downloads the JSON I need. Just to clarify, the JSON isn't displayed on the web page itself. I want to load the data into JavaScript from the link I currently have. I've tried using the following function below, but it hasn't worked. The URL is from AWS-transcribe.
function httpGetAsync(theUrl, callback)
{
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
callback(xmlHttp.responseText);
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.send(null);
}
Does anyone know how to get a JavaScript object from this URL?
You can use the new ES6 fetch API to send a HTTP request and parse the stringified JSON response.
1. Using ES2017 async-await
const url = `https://s3.us-west-2.amazonaws.com/aws-transcribe-us-west-2-prod/165208660162/16/asrOutput.json?X-Amz-Security-Token=FQoDYXdzEBkaDNEU%2BEwhAVoss9PKYyK3A2qPjQJuSJOukAWY%2BTcHUY8vJoq9xQdb2%2FwEYalKy2uDvgEo03cgqeGAp%2Bjg7WGLNi4nr%2Bv5K2yOYMNG1WNmjbC8pWWArkrgGwCr8%2FXLZCxgziieoi1nV9BToNYEaSXI%2BpUR2w50o1T7a%2FY%2FQkL6hBO%2BIr3%2Fr9KYtWk%2BvC5dJryzW%2BxuHjXVv3SP%2F3SsYrqMuFLgqWct3Msvo37or4S7IxGDg6wcFzutLLQ2RrNXXa77oZJ3C%2BLn7t%2FTmthj3IJXT%2F8%2FlNEYMGc6WUG6aEqC%2F3iQmq6Pg6HDNRYQytIg0OelRhBJ7PzF0spwssUo2ZnuuRQKu%2B%2BuFbnp1Ne2B9PNDDIRRQF1bHKydwRiI%2BfIBH%2FRk3SnFH6dzppvdQjjOlZQzaVYsEE4aWJZ1UA%2FGtZlwID%2FU301m3XjdNebnH%2FgeCCrRKs1U51obmXyXeNP0veCXzgFexrf6JApN7bRrBOpWNp%2BlBXXeHI0YPa07zL62JerTwbLmSXzxbqbMSjso0t2LygCWhvsKXry5gig06%2FtyuLf%2Fxi0GRKKrDlpR2Kg%2BHGE%2B1cCbxFT3jPaIZPy1ktScZpxMw89g0kojO7W1wU%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20180511T164920Z&X-Amz-SignedHeaders=host&X-Amz-Expires=900&X-Amz-Credential=ASIAJHSTNMRIX5LPMVVQ%2F20180511%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Signature=586c9c3f4a1a755757e8cd20e77fa1318cf769fce71d3167a885b209d56e537f`;
async function downloadObject(url) {
try {
const fetchResponse = await fetch(url);
return await fetchResponse.json();
} catch (err) {
console.error('Error - ', err);
}
}
downloadObject(url);
2. Using chained then and catch blocks
const url = `https://s3.us-west-2.amazonaws.com/aws-transcribe-us-west-2-prod/165208660162/16/asrOutput.json?X-Amz-Security-Token=FQoDYXdzEBkaDNEU%2BEwhAVoss9PKYyK3A2qPjQJuSJOukAWY%2BTcHUY8vJoq9xQdb2%2FwEYalKy2uDvgEo03cgqeGAp%2Bjg7WGLNi4nr%2Bv5K2yOYMNG1WNmjbC8pWWArkrgGwCr8%2FXLZCxgziieoi1nV9BToNYEaSXI%2BpUR2w50o1T7a%2FY%2FQkL6hBO%2BIr3%2Fr9KYtWk%2BvC5dJryzW%2BxuHjXVv3SP%2F3SsYrqMuFLgqWct3Msvo37or4S7IxGDg6wcFzutLLQ2RrNXXa77oZJ3C%2BLn7t%2FTmthj3IJXT%2F8%2FlNEYMGc6WUG6aEqC%2F3iQmq6Pg6HDNRYQytIg0OelRhBJ7PzF0spwssUo2ZnuuRQKu%2B%2BuFbnp1Ne2B9PNDDIRRQF1bHKydwRiI%2BfIBH%2FRk3SnFH6dzppvdQjjOlZQzaVYsEE4aWJZ1UA%2FGtZlwID%2FU301m3XjdNebnH%2FgeCCrRKs1U51obmXyXeNP0veCXzgFexrf6JApN7bRrBOpWNp%2BlBXXeHI0YPa07zL62JerTwbLmSXzxbqbMSjso0t2LygCWhvsKXry5gig06%2FtyuLf%2Fxi0GRKKrDlpR2Kg%2BHGE%2B1cCbxFT3jPaIZPy1ktScZpxMw89g0kojO7W1wU%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20180511T164920Z&X-Amz-SignedHeaders=host&X-Amz-Expires=900&X-Amz-Credential=ASIAJHSTNMRIX5LPMVVQ%2F20180511%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Signature=586c9c3f4a1a755757e8cd20e77fa1318cf769fce71d3167a885b209d56e537f`;
const successCb = (resp) => {
console.log(resp);
};
const errorCb = (err) => {
console.error('Error - ', err);
};
function downloadObject(url, successCb, errorCb) {
fetch(url)
.then(response => response.json())
.then(successCb)
.catch(errorCb);
}
downloadObject(url, successCb, errorCb);

Categories

Resources