使用 fetch 上传多个文件

upload multiple files with fetch

我尝试使用 reactjs 和 flask 每次接收多个文件 api,但我收到此错误

data = file.read()
AttributeError: 'str' object has no attribute 'read'

使用 reactjs 的 fontend 代码

this.state = {
  file : []
}

fileSelectHandler = (event) => {
  var totalfiles = event.target.files.length;
  for (var index=0; index < totalfiles; index++){
    this.state.file.push(event.target.files[index])
  }
}
    
async onFormSubmit (event) {
   event.preventDefault();
   const formData = new FormData();
   formData.append("file", this.state.file);    

   await fetch(apiUrl+'photo/create' , {
      method: 'POST',
      body: formData
   })
}

后端代码使用 flask

@app.route('/photo/create', methods=['POST'])
def create_photo():
   files = request.form.getlist("file")
   print(files)

   for file in files:
      data = file.read()

flask 以 ['[object File],[object File]'] 的形式接收文件。

我试图找到读取目标文件的方法,但没有任何效果 谁能帮忙..

@app.route('/photo/create', methods=['POST', 'PUT']) # new
def create_photo():
   files = flask.request.form.getlist("file")
   print(files) # use loggers

   for file in files:                                            
      if isinstance(file, str): # check if the file is a string as in path to the file         
          with open(f"{file}", encoding='utf-8') as f: # read
              yield f.read() # yielding is an option but then you need to consume the data differently on the frontend.

我建议阅读阅读文本文件的文档。如果您正在阅读图像,那么我们正在谈论的完全是另一回事。一些上下文会更好。

我不想听起来 说教,但在部署 API 之前测试它们。如果测试不是一个选项,Redoc 或 Swagger 是优秀的文档工具——文档应该足够了。

我不使用 React 但对我来说问题

formData.append("file", this.state.file);    

因为 this.state.file 是一个或多个文件的列表,它需要 for 循环将每个文件添加为 Form

中的分离对象
const formData = new FormData();

var totalfiles = this.state.file.length;

for (var index=0; index < totalfiles; index++){
  formData.append("file", this.state.file[index]);    
}

现在它在 request.files 中发送所有文件,而不是在 request.form

中发送一个(无用的)字符串 '[object File],[object File]'

最少的工作代码

from flask import Flask, request, render_template_string

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def index():
    return render_template_string('''
<!DOCTYPE html>

<script>
this.state = {
  file: []
}

fileSelectHandler = (event) => {
  console.log("fileSelectHandler");
  var totalfiles = event.target.files.length;
  for (var index=0; index < totalfiles; index++){
    this.state.file.push(event.target.files[index]);
  }
}
    
async function onFormSubmit(event) {
  console.log("onFormSubmit");
  event.preventDefault();
   
  const formData = new FormData();

  var totalfiles = this.state.file.length;
  for (var index=0; index < totalfiles; index++){
    formData.append("file", this.state.file[index]);    
  }
  
  // doesn't need `http://.../` if sends to the same server
  await fetch('/photo/create',  
  {
    method: 'POST',
    body: formData,
  });
}
</script>

<input type="file" multiple onChange="fileSelectHandler(event);">
<button type="submit" onClick="onFormSubmit(event);">Submit</button>
''')

@app.route('/photo/create', methods=['GET', 'POST'])
def photo():
    print('args :', request.args)
    print('form :', request.form)
    print('json :', request.json)
    print('files:', request.files)
    for file in request.files.getlist('file'):
        print(file.filename)
        #print(file.read())
    return render_template_string(''' ''')

if __name__ == '__main__':
    #app.debug = True 
    app.run()