How to upload an array of images - javascript

I have these methods for choosing and uploading the image, but I am not sure about where the error is, if it is in the code or in the API
const [imagens, setImagens] = useState([])
function choseImage(event) {
imagens.push(URL.createObjectURL(event.target.files[0]))
}
const upload = () => {
const fd = new FormData();
// imagens is the array of images
fd.append('imagem', imagens)
fetch('http://localhost:5000/api/Upload', fd)
.then(res => {
console.log(res)
});
}
here is the UploadController, the messege is:
System.NullReferenceException: 'Object reference not set to an instance of an object.' file was null.
[HttpPost]
public async Task Post(IFormFile file)
{
var nomeArquivo = (file.FileName);
var uploads = Path.Combine(Directory.GetCurrentDirectory(), "wwwRoot\\Upload", nomeArquivo);
if (!Directory.Exists(uploads)) Directory.CreateDirectory(uploads);
if (file.Length > 0)
{
using (var fileStream = new FileStream(Path.Combine(uploads, file.FileName), FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
}
}
this is my first freelance project, that is the last thing missing, Thanks in advance

Your code is nearly complete with a couple of changes commented in the code below. In your React component you can do something like the following.
export const ImageFileUpload = () => {
const [images, setImages] = useState([]);
const onFileChange = (files) => {
setImages(f => [...f, ...files]);
};
const handleClick = (e) => {
e.preventDefault();
const formData = new FormData();
for (let i = 0; i < images.length; i++) {
// 'images' name of the formData values must match the action method param on your controller
formData.append(`images`, images[i]);
}
// Make sure you api is running on the same endpoint or you need to set up CORS
fetch('https://localhost:5001/fileupload', {
body: formData,
method: "POST" // Make sure you add the method POST to your request when API is only accepting POST
}).then(res => console.log(res));
};
return (
<form>
<input type="file" multiple={true} onChange={e => onFileChange(e.target.files)} />
<button onClick={handleClick}>Upload</button>
</form>
)
};
Your controller should look like this.
[ApiController]
[Route("[controller]")]
public class FileUploadController : ControllerBase
{
[HttpPost]
// 1. The name of the parameter must match the name of the `formData`
// 2. This method should take `List<IFormFile>`
public async Task Upload(List<IFormFile> images)
{
foreach (var image in images)
{
var nomeArquivo = (image.FileName);
var uploads = Path.Combine(Directory.GetCurrentDirectory(), "wwwRoot\\Upload\\", nomeArquivo);
if (!Directory.Exists(uploads)) Directory.CreateDirectory(uploads);
using (var fileStream = new FileStream(Path.Combine(uploads, image.FileName), FileMode.Create))
{
await image.CopyToAsync(fileStream);
}
}
}
}

Related

How to handle file upload if FormData when testing using Cypress?

I'm using VueJS and Cypress. I have a modal where I submit a FormData with different filled and a file:
var formData = new FormData();
formData.append("document_file", this.documentFile);
formData.append("comments", this.comments.value);
...
They way I upload it:
this.$http.post('http://localhost:8081/api/upload',formData,{emulateJSON: true},{
header:{ "Content-Type":"multipart/form-data" },
}).then(function(response) {
// ... Code
}).catch((err) => {
// .. Code
});
}
I want to use Cypress to test the params. Now, when I don't use the document file in the formData, I have the following methods to parse the multipart/form-data; boundary=---:
function parse(request) {
console.log("request");
console.log(request);
const headers = request.headers;
const body = request.body;
const content_type = headers['content-type'];
expect(content_type, 'boundary').to.match(/^multipart\/form-data; boundary=/);
const boundary = content_type.split('boundary=')[1];
const values = p(boundary, body);
return values;
}
function p(boundary, body) {
expect(boundary, 'boundary').to.be.a('string');
expect(body, 'body').to.be.a('string');
const parts = body.split(`--${boundary}`).map((s) => s.trim()).filter((s) => s.startsWith('Content-Disposition: form-data;'));
const result = {};
parts.forEach((part) => {
const lines = part.split(/\r?\n/g);
const key = lines[0].match(/name="(.+)"/)[1];
result[key] = (lines.length >= 2) ? lines[2].trim() : "";
});
return result;
}
Which work great. But when I upload the file, I get a different request.body:
They way I'm trying to test:
cy.get('#createDocument').then((interception) => {
assert.isNotNull(interception);
const values = parse(interception.request);
assert.isTrue(values.comments === "");
});
How can I handle this?

Building an Object from fetch statement

I have some code that when you console.log it, it looks like the image below:
The code I am running is as follows:
onClick={() => {
const stream = fetch(
'https://lichess.org/api/games/user/neio',
{ headers: { Accept: 'application/x-ndjson' } }
);
const onMessage = obj => {
console.log('test', obj);
};
const onComplete = () =>
console.log('The stream has completed');
stream.then(readStream(onMessage)).then(onComplete);
}}
export const readStream = processLine => response => {
const stream = response.body.getReader();
const matcher = /\r?\n/;
const decoder = new TextDecoder();
let buf = '';
const loop = () =>
stream.read().then(({ done, value }) => {
if (done) {
if (buf.length > 0) processLine(JSON.parse(buf));
} else {
const chunk = decoder.decode(value, {
stream: true,
});
buf += chunk;
const parts = buf.split(matcher);
buf = parts.pop();
for (const i of parts) processLine(JSON.parse(i));
return loop();
}
});
return loop();
};
export default readStream;
What I am trying to do is build a parent object that contains all these individual rows of data.
I'm new at promises and fetch etc. So currently, I have no idea on how to build this parent object that contains each individual row.
Any suggestions?
Can't you have a global array and add items to it like:
var arrCollection = [];
...
const onMessage = obj => {
arrCollection.push(obj);
};
You can have an object with those items doing like:
var objCollection = { items: arrCollection };

How to pass object from one file to another in javascript?

I have node js server file (index.js) and client file (orderlist.js)
In index.js i am getting promise object , like that
function returnOrderArray() {
var i = 0;
const promise = new Promise((resolve, reject) => {
connection.query('SELECT * FROM orders', function(error, results) {
while (i < results.length) {
order.id[i] = results[i].id;
order.wavetype[i] = results[i].wavetype;
order.color[i] = results[i].color;
order.thick[i] = results[i].thick;
order.readydate[i] = results[i].readydate;
order.createdate[i] = results[i].createdate;
order.manager[i] = results[i].manager;
i++;
}
resolve(order);
// console.log(order);
});
});
return promise;
}
then i want to pass it to other js file.
I tried to do that with module.exports
app.get('/orderlist', checkUserSession, async function(request, response) {
returnOrderArray().catch(error => console.log(error)).then((() => {
module.exports.order = order;
response.render("orderlist.ejs", { username: request.session.username });
})).catch(error => console.log(error));
});
and then import it in orderlist.js
var ind = require('../../index')
function asd() {
alert(ind.order);
}
but it seems not to work.
What am i doing wrong , and what's the best way to pass objects to other files in js?
oh , and file architecture
filearch
You need to export your module like so: module.exports = returnOrderArray
try this,
orderlist.js
const returnOrderArray = () => {...some code..}
module.exports = returnOrderArray
index.js
const returnOrderArray = require('./path/to/orderlist.js')
const run = async() => {
const orderlist = await returnOrderArray() // use await or then as you prefer
}
run()
async_await link if curious!
Hope this will work :)

Wait for angularfire storage upload

I'm doing the "complete your profile" part of my app and I want to upload some images to Firebase.
It is all done in 2 methods located in my "auth" service. I'm having issues getting the data from the uploads, this is the code so far:
async updateUserProfile(
profilePicture: File,
name: string,
birthdate: Date,
countryCode: string,
photoID: File
) {
let updatedAppUser: authenticatedUser;
this.appUser.pipe(take(1)).subscribe((currentAppUser) => {
updatedAppUser = currentAppUser;
});
const uploadPackage = new FormData();
uploadPackage.append(updatedAppUser.UID, profilePicture);
uploadPackage.append(updatedAppUser.UID + "_", photoID);
let uploadedData = await this.fileUpload(uploadPackage);
let profilePicturePath: string;
let photoIDPath: string;
//**********************************************
//HERE IS THE PROBLEM-- I THINK THIS IS WRONG
//**********************************************
if (uploadedData) {
profilePicturePath = uploadedData[0];
photoIDPath = uploadedData[1];
}
//TO-DO: call backend and update the user profile
//after success from backend call
//console.log("photoID Path: ", photoIDPath);
updatedAppUser.showKYC = false;
updatedAppUser.userProfilePicture = profilePicturePath;
updatedAppUser.isPendingValidation = true;
updatedAppUser.userName = name;
updatedAppUser.userBirthdate = birthdate;
updatedAppUser.userCountryCode = countryCode;
//save to local storage
this.storeAuthData(updatedAppUser);
//new updated appuser
this.appUser.next(updatedAppUser);
}
And this is the method I'm using to upload data to Firebase:
private async fileUpload(data: FormData) {
const filePaths: string[] = [];
const promises: AngularFireUploadTask[] = [];
for (const value of data.entries()) {
const uploadTask = this.firebaseStorage.ref(value[0]).put(value[1]);
promises.push(uploadTask);
}
const promiseArray = await Promise.all(promises);
if (promiseArray) {
promiseArray.forEach(async (filePromise) => {
filePaths.push(await filePromise.ref.getDownloadURL());
});
return filePaths;
}
}
I'd probably use a second Promise.all for the download URL retrievals, and remove the use of await since it makes things confusing:
private async fileUpload(data: FormData) {
const promises: AngularFireUploadTask[] = [];
for (const value of data.entries()) {
const uploadTask = this.firebaseStorage.ref(value[0]).put(value[1]);
promises.push(uploadTask);
}
Promise.all(promises).then((tasksArray) => {
const filePaths = tasksArray.map((task) => task.ref.getDownloadURL());
return Promise.all(filePaths);
}
}

Angular 2 Synchronous File Upload

I am trying to upload a file to web api which takes the file as byte array using angular 2 application.
I am not able to pass the byte array from angular 2 page to web api. It looks like the File Reader read method is asynchronous. How do I make this as synchronous call or wait for the file content to be loaded before executing the next line of code?
Below is my code
//attachment on browse - when the browse button is clicked
//It only assign the file to a local variable (attachment)
fileChange = (event) => {
var files = event.target.files;
if (files.length > 0) {
this.attachment = files[0];
}
}
//when the submit button is clicked
onSubmit = () => {
//Read the content of the file and store it in local variable (fileData)
let fr = new FileReader();
let data = new Blob([this.attachment]);
fr.readAsArrayBuffer(data);
fr.onloadend = () => {
this.fileData = fr.result; //Note : This always "undefined"
};
//build the attachment object which will be sent to Web API
let attachment: Attachment = {
AttachmentId: '0',
FileName: this.form.controls["attachmentName"].value,
FileData: this.fileData
}
//build the purchase order object
let order: UpdatePurchaseOrder = {
SendEmail: true,
PurchaseOrderNumber: this.form.controls["purchaseOrderNumber"].value,
Attachment: attachment
}
//call the web api and pass the purchaseorder object
this.updatePoService
.updatePurchaseOrder(this.form.controls["purchaseOrderRequestId"].value, order)
.subscribe(data => {
if (data) {
this.saveSuccess = true;
}
else {
this.saveSuccess = false;
}
},
error => this.errors = error,
() => this.res = 'Completed'
);
}
Any hint would be useful.
regards,
-Alan-
You cannot make this async call synchronous. But you can take advantage of the observables to wait for the files to be read:
//when the submit button is clicked
onSubmit = () => {
let file = Observable.create((observer) => {
let fr = new FileReader();
let data = new Blob([this.attachment]);
fr.readAsArrayBuffer(data);
fr.onloadend = () => {
observer.next(fr.result);
observer.complete()
};
fr.onerror = (err) => {
observer.error(err)
}
fr.onabort = () => {
observer.error("aborted")
}
});
file.map((fileData) => {
//build the attachment object which will be sent to Web API
let attachment: Attachment = {
AttachmentId: '0',
FileName: this.form.controls["attachmentName"].value,
FileData: fileData
}
//build the purchase order object
let order: UpdatePurchaseOrder = {
SendEmail: true,
PurchaseOrderNumber: this.form.controls["purchaseOrderNumber"].value,
Attachment: attachment
}
return order;
})
.switchMap(order => this.updatePoService.updatePurchaseOrder(this.form.controls["purchaseOrderRequestId"].value, order))
.subscribe(data => {
if (data) {
this.saveSuccess = true;
} else {
this.saveSuccess = false;
}
},
error => this.errors = error,
() => this.res = 'Completed'
);
}
I arrived here looking for a solution for a similar issue. I'm performing requests to an endpoint which can response a binary blob if anything goes well or a JSON file in event of error.
this.httpClient.post(urlService, bodyRequest,
{responseType: 'blob', headers: headers})
.pipe(map((response: Response) => response),
catchError((err: Error | HttpErrorResponse) => {
if (err instanceof HttpErrorResponse) {
// here, err.error is a BLOB containing a JSON String with the error message
} else {
return throwError(ErrorDataService.overLoadError(err, message));
}
}));
As FileReaderSync apparently doesn't work in Angular6 I took n00dl3's solution (above) to throw the error after parsing the Blob content:
return this.httpClient.post(urlService, bodyRequest,
{responseType: 'blob', headers: headers})
.pipe(map((response: Response) => response),
catchError((err: Error | HttpErrorResponse) => {
const message = `In TtsService.getTts(${locale},${outputFormat}). ${err.message}`;
if (err instanceof HttpErrorResponse) {
const $errBlobReader: Observable<HttpErrorResponse> = Observable.create((observer) => {
const fr = new FileReader();
const errorBlob = err.error;
fr.readAsText(errorBlob, 'utf8');
fr.onloadend = () => {
const errMsg = JSON.parse(fr.result).message;
const msg = `In TtsService.getTts(${locale},${outputFormat}). ${errMsg}`;
observer.error(ErrorDataService.overLoadError(err, msg));
};
fr.onerror = (blobReadError) => {
observer.error(blobReadError);
};
fr.onabort = () => {
observer.error('aborted');
};
});
return $errBlobReader;
} else {
return throwError(ErrorDataService.overLoadError(err, message));
}
}));
Thanks! You really saved my day!

Categories

Resources