-2

I am trying to receive more than one file per time using reactjs and flask api, but I get this error

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

fontend code using reactjs

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
   })
}

backend code using 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 receiving the files as ['[object File],[object File]'] .

I tried to find ways to read an object file but nothing worked can anyone help..

furas
  • 134,197
  • 12
  • 106
  • 148
  • maybe you should use `request.file` instead of `request.form`. – furas Jan 21 '22 at 02:14
  • when I use request.files.getlist("file") I get an empty array – Ibraheem Alibraheem Jan 21 '22 at 02:34
  • I have only example how to upload image from webcam using `fetch` and `request.files`. I don't know what you really send but it seems you send only filename without file data - and your variable `file` has only string. OR maybe it has already file data as string (`BASE64`) and you should first use `print()` to see what you really have in variable (before you try to use `.read()`). It is called `"print debuging"`. With `request.form` you can't get file object but only string. Only `request.files` can convert data to file object which you can `read()` – furas Jan 21 '22 at 02:41
  • it would be simpler if you would create minimal working code (with all code in one file) which we could simply copy and run. – furas Jan 21 '22 at 02:44
  • 1
    BWT: if `file` in JavaScript is a list with many files then you should send every file as separated element in `form` but you append all list as one element - `formData.append("file", this.state.file);` - and later flask may get it as some string with list instead of expected files. See [example in Mozilla doc](https://developer.mozilla.org/en-US/docs/Web/API/FormData/append#example) – furas Jan 21 '22 at 02:58

2 Answers2

2

I don't use React but for me problem makes

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

because this.state.file is list with one or many files and it needs for-loop to add every file as separated object in 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]);    
}

And now it sends all files in request.files instead of one (useless) string '[object File],[object File]' in request.form


Minimal working code

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()  
furas
  • 134,197
  • 12
  • 106
  • 148
-2
@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.

I would recommend reading the docs of reading text files. If you're reading images, we are talking about something else altogether. Some context would be better.

I don't want to sound preachy, but test the APIs before deploying them. Redoc or Swagger are excellent documenting tools if testing isn't an option -- docs should suffice.

  • with your solution I get this error TypeError: The view function did not return a valid response. The return type must be a string, dict, tuple, Response instance, or WSGI callable, but it was a generator. – Ibraheem Alibraheem Jan 21 '22 at 02:40