#javascript #node.js #asynchronous #async-await #multipartform-data
Вопрос:
Я пытаюсь отправить POST
запрос на свой сервер, для этого POST
требуется запрос multipart/form-data
, поэтому я преобразую местоположения изображений в Buffers
, прежде чем отправлять все это в качестве POST
запроса, но из-за async
поведения Nodejs я не знаю, как заставить функции работать по порядку. Как я должен изменить/переписать этот код?
import axios from "axios";
import { readFileSync, readFile as rf, PathOrFileDescriptor } from "fs";
import FormData from "form-data";
import dotenv from "dotenv";
dotenv.config();
// readfile promise
const readFile = (file: PathOrFileDescriptor) => {
return new Promise((resolve, reject) => {
rf(file, (err, data) => {
if (err) reject(err.message);
resolve(data);
});
});
};
console.log("Parsing JSON data 💾");
const jsonData = readFileSync("data/data.json").toString();
const products: any[] = JSON.parse(jsonData).products;
console.log("Products Store in JavaScript Object 🛒");
products.forEach((product: any, index: number) => {
console.log(
`Creating Product ${index 1}/${products.length} - ${product.name}`
);
let requestData = new FormData();
for (const [key, value] of Object.entries<string>(product)) {
if (key.includes("image")) {
console.log(`Converting ${key} in Image Blob 🖼`);
readFile(value)
.then((data) => {
requestData.append(key, data);
console.log(`${key} Converted ✔`);
})
.catch((err) => {
console.log(`${key} Failed to Convert ❌ - Error=${err}`);
});
} else {
if (key === "variants") {
requestData.append(key, JSON.stringify(value));
} else {
requestData.append(key, value);
}
}
}
axios
.post("http://localhost:8000/api/v1/product/create", requestData, {
headers: {
"Content-Type": "multipart/form-data",
Cookie: `access_token=${process.env.ACCESS_TOKEN}; csrftoken=${process.env.CSRF_TOKEN}`,
},
withCredentials: true,
})
.then(() => {
console.log(`Product - ${product.name} Created ✔`);
})
.catch((err) => {
console.log(`Product - ${product.name} Failed to create ❌`);
if (err.response) {
console.log(err.response.data);
} else {
console.log("Internal server error");
}
});
});
Данные, которые я анализирую, представляют собой массив объектов, подобных этому
{
"store": "maki-2",
"name": "Nike Sportwear Down-fill Windrunner Jacket",
"description": "The Nike Sportswear Jacket warms up your winter wardrobe with a serious supply of down. This lightweight zip-up style features water-resistant and windproof Nike Shield technology to help keep you comfortable in rough weather. A subtle chevron design, which references the OG Windrunner, graces the chest.",
"brand": "Nike",
"gender": "U",
"category": "outwear",
"image_v0_1": "data/images/outwear/Nike Sportswear Down-Fill Windrunner/sportswear-down-fill-windrunner-jacket-hHNjxL (3).png",
"image_v0_2": "data/images/outwear/Nike Sportswear Down-Fill Windrunner/sportswear-down-fill-windrunner-jacket-hHNjxL.jpg",
"image_v0_3": "data/images/outwear/Nike Sportswear Down-Fill Windrunner/sportswear-down-fill-windrunner-jacket-hHNjxL.png",
"image_v1_1": "data/images/outwear/Nike Sportswear Down-Fill Windrunner/sportswear-down-fill-windrunner-jacket-hHNjxL (1).jpg",
"image_v1_2": "data/images/outwear/Nike Sportswear Down-Fill Windrunner/sportswear-down-fill-windrunner-jacket-hHNjxL (1).png",
"image_v1_3": "data/images/outwear/Nike Sportswear Down-Fill Windrunner/sportswear-down-fill-windrunner-jacket-hHNjxL (2).png",
"variants": [
{
"is_default": false,
"price": 23000,
"quantity": 23,
"size": "M",
"color": "multi-colored"
},
{
"is_default": true,
"price": 25000,
"quantity": 23,
"size": "L",
"color": "black"
}
]
},
Комментарии:
1. Если вы хотите читать файл синхронно, вы можете использовать это: nodejs.org/api/fs.html#fs_fs_readfilesync_path_options
Ответ №1:
Вы можете использовать Promise.all
, чтобы дождаться завершения всех обещаний внутри функции карты:
import axios from "axios";
import {
readFileSync,
readFile as rf,
PathOrFileDescriptor
} from "fs";
import FormData from "form-data";
import dotenv from "dotenv";
dotenv.config();
// readfile promise
const readFile = (file: PathOrFileDescriptor) => {
return new Promise((resolve, reject) => {
rf(file, (err, data) => {
if (err) reject(err.message);
resolve(data);
});
});
};
console.log("Parsing JSON data 💾");
const jsonData = readFileSync("data/data.json").toString();
const products: any[] = JSON.parse(jsonData).products;
console.log("Products Store in JavaScript Object 🛒");
await Promise.all(
products.map(async(product: any, index: number) => {
console.log(
`Creating Product ${index 1}/${products.length} - ${product.name}`
);
let requestData = new FormData()
Object.keys(product).map(async(key: any) => {
if (key.includes("image")) {
try {
const data = await readFile(product[key])
requestData.append(key, data)
console.log(`${key} Converted ✔`);
} catch (e) {
console.log(`${key} Failed to Convert ❌ - Error=${e}`);
}
} else {
if (key === "variants") {
requestData.append(key, JSON.stringify(value));
} else {
requestData.append(key, value);
}
}
return key;
});
await axios
.post("http://localhost:8000/api/v1/product/create", requestData, {
headers: {
"Content-Type": "multipart/form-data",
Cookie: `access_token=${process.env.ACCESS_TOKEN}; csrftoken=${process.env.CSRF_TOKEN}`,
},
withCredentials: true,
})
.then(() => {
console.log(`Product - ${product.name} Created ✔`);
})
.catch((err) => {
console.log(`Product - ${product.name} Failed to create ❌`);
if (err.response) {
console.log(err.response.data);
} else {
console.log("Internal server error");
}
});
return product;
})
);
Ответ №2:
Вы должны связать обещания в цепочку в цикле for.
var readFilePromise = Promise.resolve();
for (const [key, value] of Object.entries < string > product) {
if (key.includes("image")) {
console.log(`Converting ${key} in Image Blob 🖼`);
readFilePromise = readFilePromise
.then(() => readFile(value))
.then((data) => {
requestData.append(key, data);
console.log(`${key} Converted ✔`);
})
.catch((err) => {
console.log(`${key} Failed to Convert ❌ - Error=${err}`);
});
} else {
if (key === "variants") {
requestData.append(key, JSON.stringify(value));
} else {
requestData.append(key, value);
}
}
}
и ваш POST
запрос должен быть сделан только после того, как обещание будет выполнено.
readFilePromise.then(() => {
axios.post(...)
})