使用 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 只捕获了最后一个文件。