使用 Fetch 和 FormData API 上传多个文件
Uploading multiple files with Fetch and FormData APIs
我正在尝试使用本机 Fetch and FormData API 一次将多个文件上传到服务器,但我终究无法让它工作。这是我得到的:
// acceptedFiles are File objects coming from `react-dropzone`.
function handleSubmit(acceptedFiles) {
const data = new FormData();
for (const file of acceptedFiles) {
data.append('files', file, file.name);
}
return fetch('https://example.com/api/upload', {
method: 'POST',
body: data,
});
}
但是我的 Rails 服务器收到的是这样的:
Parameters: {"files"=>#
<ActionDispatch::Http::UploadedFile:0x00007feb347becc0 @tempfile=#
<Tempfile:/var/folders/kl/y1jrp7zs55sbx075jjjl3p280000gn/T/RackMultipart201
71112-6486-1ftkufy.mp4>, @original_filename="SampleVideo_1280x720_5mb.mp4",
@content_type="video/mp4", @headers="Content-Disposition: form-data;
name=\"files\"; filename=\"SampleVideo_1280x720_5mb.mp4\"\r\nContent-Type:
video/mp4\r\n">}
换句话说,看起来files
实际上只是一个文件。但是docs for FormData
say that append should append multiple files。
怎么了?
解决方案是将 files
更改为 files[]
:
// acceptedFiles are File objects coming from `react-dropzone`.
function handleSubmit(acceptedFiles) {
const data = new FormData();
for (const file of acceptedFiles) {
data.append('files[]', file, file.name);
}
return fetch('https://example.com/api/upload', {
method: 'POST',
body: data,
});
}
对于发送多个文件,我做的有点不同,因为我的 php api 端点说如果我用 formdata.append('files[]', file)
[=15= 发送它只有一个文件]
<input type="file" multiple data-sender="{{ user.hashIdentifier }}">
/** @type {HTMLInputElement} */
const input = document.querySelector('input[type=file]');
input.addEventListener('input', function listener(event){
if(input.files.length > 0){
const formData = new FormData();
for(let i = 0; i < input.files.lenght; i++){
formData.append(`file_${i}`, input.files[i]);
}
// my endpoint should know who sends the file
formData.append('identify', input.dataSet.sender);
sendData(formData);
} else {
console.error('no files selected');
}
});
/**
* sending the formData Object with all files to the server
* @param {FormData} formData
*/
function sendData(formData) {
const url = '/api/saveFiles';
const options = {
method: 'POST',
body: formData
};
fetch(url, options)
.fetch((response) => {
if(!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
.fetch((data) => {
console.log('success', data);
})
.catch((error) => {
console.error(error);
})
}
我的 php 是一个 symfony 项目,看起来像这样:
/**
* @param Request $request (inject symfonys http request)
* @param FileService $fileService (inject my own FileService)
* @returns Response (symfony http Response)
*/
#[Route('/api/saveFiles', name: 'api_saveFiles', methods: ['POST'])]
public function api_saveFiles(Request $request, FileService $fileService): Response
{
$statusCode = 409; // Conflict
$result = array();
/** @var FileBag */
$fileBag = $request->files;
if($fileBag->count() > 0) {
$statusCode=200;
$result['numberOfFiles'] = $fileBag->count();
$result['keys'] = $fileBag->keys();
$result['infos'] = array();
foreach($fileBag->keys() as $key){
try {
$file = $fileBag->get($key);
$fileInfo = $fileService->saveFile($file); // saves the file and gives back some infos
$result['infos'][$fileInfo];
} catch(Exception $ex) {
// TODO: handle errors
}
}
}
return new JsonResponse($result, $statusCode);
}
在尝试中我用 formData.append('files[]', file);
php 只捕获了最后一个文件。
我正在尝试使用本机 Fetch and FormData API 一次将多个文件上传到服务器,但我终究无法让它工作。这是我得到的:
// acceptedFiles are File objects coming from `react-dropzone`.
function handleSubmit(acceptedFiles) {
const data = new FormData();
for (const file of acceptedFiles) {
data.append('files', file, file.name);
}
return fetch('https://example.com/api/upload', {
method: 'POST',
body: data,
});
}
但是我的 Rails 服务器收到的是这样的:
Parameters: {"files"=>#
<ActionDispatch::Http::UploadedFile:0x00007feb347becc0 @tempfile=#
<Tempfile:/var/folders/kl/y1jrp7zs55sbx075jjjl3p280000gn/T/RackMultipart201
71112-6486-1ftkufy.mp4>, @original_filename="SampleVideo_1280x720_5mb.mp4",
@content_type="video/mp4", @headers="Content-Disposition: form-data;
name=\"files\"; filename=\"SampleVideo_1280x720_5mb.mp4\"\r\nContent-Type:
video/mp4\r\n">}
换句话说,看起来files
实际上只是一个文件。但是docs for FormData
say that append should append multiple files。
怎么了?
解决方案是将 files
更改为 files[]
:
// acceptedFiles are File objects coming from `react-dropzone`.
function handleSubmit(acceptedFiles) {
const data = new FormData();
for (const file of acceptedFiles) {
data.append('files[]', file, file.name);
}
return fetch('https://example.com/api/upload', {
method: 'POST',
body: data,
});
}
对于发送多个文件,我做的有点不同,因为我的 php api 端点说如果我用 formdata.append('files[]', file)
[=15= 发送它只有一个文件]
<input type="file" multiple data-sender="{{ user.hashIdentifier }}">
/** @type {HTMLInputElement} */
const input = document.querySelector('input[type=file]');
input.addEventListener('input', function listener(event){
if(input.files.length > 0){
const formData = new FormData();
for(let i = 0; i < input.files.lenght; i++){
formData.append(`file_${i}`, input.files[i]);
}
// my endpoint should know who sends the file
formData.append('identify', input.dataSet.sender);
sendData(formData);
} else {
console.error('no files selected');
}
});
/**
* sending the formData Object with all files to the server
* @param {FormData} formData
*/
function sendData(formData) {
const url = '/api/saveFiles';
const options = {
method: 'POST',
body: formData
};
fetch(url, options)
.fetch((response) => {
if(!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
.fetch((data) => {
console.log('success', data);
})
.catch((error) => {
console.error(error);
})
}
我的 php 是一个 symfony 项目,看起来像这样:
/**
* @param Request $request (inject symfonys http request)
* @param FileService $fileService (inject my own FileService)
* @returns Response (symfony http Response)
*/
#[Route('/api/saveFiles', name: 'api_saveFiles', methods: ['POST'])]
public function api_saveFiles(Request $request, FileService $fileService): Response
{
$statusCode = 409; // Conflict
$result = array();
/** @var FileBag */
$fileBag = $request->files;
if($fileBag->count() > 0) {
$statusCode=200;
$result['numberOfFiles'] = $fileBag->count();
$result['keys'] = $fileBag->keys();
$result['infos'] = array();
foreach($fileBag->keys() as $key){
try {
$file = $fileBag->get($key);
$fileInfo = $fileService->saveFile($file); // saves the file and gives back some infos
$result['infos'][$fileInfo];
} catch(Exception $ex) {
// TODO: handle errors
}
}
}
return new JsonResponse($result, $statusCode);
}
在尝试中我用 formData.append('files[]', file);
php 只捕获了最后一个文件。