我想做的是将文件从前端上传到我的 GCP 工作流程,并且它正确地执行了此操作,但是我不断遇到
API resolved without sending a response
问题。我尝试将 form.parse()
放入 Promise
中,但这也不能有效地为我返回响应。让我更困惑的部分是我在前端控制台记录响应,如果成功,它会正确显示响应。以下是我当前的代码:
export const config = {
api: {
bodyParser: false
}
}
export default async function handler(req, res) {
const { query: { uuid } } = req
const token = await getToken({ req })
const key = JSON.parse(process.env.GOOGLE_APPLICATION_CREDENTIALS.toString())
if (!token) {
return res.status(401).json({ error: 'User must be logged in to perform this action' })
}
const storage = new Storage({
projectId: process.env.PROJECT_ID,
credentials: {
client_email: key.client_email,
private_key: key.private_key.replace(/\\n/g, '\n')
}
})
const form = formidable({
keepExtensions: true,
})
form.parse(req, async (error, fields, files) => {
if (error) {
console.error(`Error parsing form: ${error}`);
return res.status(500).json({ error: 'Error parsing form' });
}
const selectedFile = files.file[0];
if (!selectedFile) {
console.error('No file uploaded')
return res.status(400).json({ error: 'No file uploaded' })
}
const salt = crypto.randomBytes(16).toString('hex')
const hashedFilename = crypto.createHash('sha256').update(selectedFile.originalFilename + salt).digest('hex')
const bucket = storage.bucket(process.env.CLOUD_STORAGE_BUCKET_NAME)
const blob = bucket.file(hashedFilename)
const blobStream = createReadStream(selectedFile.filepath)
.pipe(blob.createWriteStream({
resumable: false,
contentType: selectedFile.mimetype
}))
blobStream.on('error', (error) => {
console.error(`Error uploading files to cloud storage: ${error}`)
cleanupStreams(blobStream, selectedFile.filepath)
return res.status(500).json({ error: 'Error uploading files to cloud storage' })
})
blobStream.on('finish', async () => {
try {
await blob.setMetadata({
metadata: {
UUID: uuid,
FILE_NAME: selectedFile.originalFilename,
CREATION_DATE: getDate(),
}
})
cleanupStreams(blobStream, selectedFile.filepath)
return res.status(201).json({ message: `Uploaded the file successfully: ${selectedFile.newFilename}` })
} catch (error) {
console.error(`Error setting metadata: ${error}`)
cleanupStreams(blobStream, selectedFile.filepath)
return res.status(500).json({ error: 'Error setting metadata' })
}
})
})
form.on('error', (error) => {
console.error(`Form parsing error: ${error}`)
return res.status(500).json({ error: 'Error processing form data' })
})
}
您所面临的问题(API 在不发送响应的情况下解决)可能与异步操作中响应的管理方式有关。更具体地说,问题可能源于文件上传和元数据设置的异步处理,这可能无法有效地向 Next.js 发出请求完成的信号。
您可以采取以下几个步骤来解决此问题👇
👉 确保完整的响应处理:在 Node.js 和 Next.js 中,必须确保函数的每个潜在执行路径都以
res.send()
、res.json()
或 res.end()
结束。您当前的实现可能缺少在某些异步分支中发出响应结束信号的信号。
👉 管理异步操作:利用 Promise 或 async/await 可以促进异步操作的更清晰管理。这可能意味着将表单解析和后续操作封装在 Promise 中,并使用 async/await 处理这些操作。
👉 使用 Async/Await 进行重构以实现更好的流程控制:重构处理程序函数以利用 async/await 来改进控制流管理可能是有益的。尝试下面更新的处理函数👇
export const config = {
api: {
bodyParser: false
}
}
export default async function handler(req, res) {
const { query: { uuid } } = req;
const token = await getToken({ req });
const key = JSON.parse(process.env.GOOGLE_APPLICATION_CREDENTIALS.toString());
if (!token) {
return res.status(401).json({ error: 'User must be logged in to perform this action' });
}
const storage = new Storage({
projectId: process.env.PROJECT_ID,
credentials: {
client_email: key.client_email,
private_key: key.private_key.replace(/\\n/g, '\n')
}
});
try {
const data = await new Promise((resolve, reject) => {
const form = formidable({ keepExtensions: true });
form.parse(req, (error, fields, files) => {
if (error) {
reject({ status: 500, error: 'Error parsing form' });
} else {
resolve(files);
}
});
});
const selectedFile = data.file[0];
if (!selectedFile) {
return res.status(400).json({ error: 'No file uploaded' });
}
const salt = crypto.randomBytes(16).toString('hex');
const hashedFilename = crypto.createHash('sha256').update(selectedFile.originalFilename + salt).digest('hex');
const bucket = storage.bucket(process.env.CLOUD_STORAGE_BUCKET_NAME);
const blob = bucket.file(hashedFilename);
const blobStream = createReadStream(selectedFile.filepath)
.pipe(blob.createWriteStream({
resumable: false,
contentType: selectedFile.mimetype
}));
blobStream.on('error', error => {
console.error(`Error uploading files to cloud storage: ${error}`);
return res.status(500).json({ error: 'Error uploading files to cloud storage' });
});
await new Promise((resolve, reject) => {
blobStream.on('finish', resolve);
blobStream.on('error', reject);
});
await blob.setMetadata({
metadata: {
UUID: uuid,
FILE_NAME: selectedFile.originalFilename,
CREATION_DATE: new Date().toISOString(),
}
});
return res.status(201).json({ message: `Uploaded the file successfully: ${selectedFile.originalFilename}` });
} catch (error) {
console.error(error);
return res.status(500).json({ error: error.message || 'An error occurred' });
}
}