简体   繁体   中英

How to send files to Django REST Framework from React?

I need to send arbitrary (eg xls ) files from React-based frontend to Django REST Framework backend.

Googled and tried many code variants for couple of hours, none of them worked completely.

Here are essential parts of code:

  1. React

1.1 Form input field

<input
    type="file"
    multiple={true}
    accept=".xls,.xlsx,.csv,.txt"
    onChange={this.handleFilesChosen}
/>

1.2 handleFilesChosen

    handleFilesChosen = event => {
        this.setState({
            files: event.target.files
        });
    }

1.3 Upload click handler ( authHeader is function substituting Authorization Bearer token)

    handleUploadClick = event => {
        let formData = new FormData();
        for (let file of this.state.files) {
            formData.append('files', file);
        }
        const csrf = this.getCookie('csrftoken');
        fetch(`${API_BASE_PATH}/load-input-data/`, {
            method: 'POST',
            headers: authHeader({contentType: 'multipart/form-data', csrf: csrf}),
            body: formData,
        })
        .then(result => result.json())
        .catch(error => error);
    }
  1. DRF View
class LoadInputDataView(APIView):
    parser_class = (MultiPartParser,)

    @method_decorator(login_required)
    def post(self, request, format=None):
        print(request.data)
        return Response(status=status.HTTP_201_CREATED)

I selected simple txt file (to make debugging easy, binary will go later) with hello world content, uploaded it and get <QueryDict: {}> in Django runserver console.

If I look at Chrome network tab, I see following empty request payload instead of real file content:

------WebKitFormBoundaryYw6ABRFkvxatzHqi
Content-Disposition: form-data; name="files"; filename="foo.txt"
Content-Type: text/plain


------WebKitFormBoundaryYw6ABRFkvxatzHqi--

Tried to remove contentType header - got 400 error with message JSON parse error (browser substitutes JSON contentType header automatically).

I'm stuck. Could anybody guide me?

Found solution. I should not set Content-Type header manually, it is set automatically with boundary option. Now Django's request.FILES work too and I could work with uploaded files from backend using code like:

class ParseInputDataView(APIView):
    parser_class = (MultiPartParser,)
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, controller_id, format=None):
        for file_entry in request.FILES.getlist('files'):
            uploaded_file_name = file_entry.name
            uploaded_file_content = file_entry.read()
            ...

I decided to maintain uniformity in the API and send the image within JSON.

In React:

const [image, setImage] = useState(null);

  const handleImageChange = (e) => {
    e.preventDefault();
    const reader = new FileReader();
    reader.onload = () => {
      var blocks = reader.result.split(";");
      const realData = blocks[1].split(",")[1];
      setImage(realData);
    };
    reader.onerror = (error) => console.error(error);
    reader.readAsDataURL(e.target.files[0]);
  };

 const onSaveHandler = () => {
    fetch(`/url`, {
        method: "post",
        credentials: "include", // send cookie with auth
        headers: {
            "Content-Type": "application/json",
            "X-CSRFToken": document.getElementById("csrf-token").value,
        }
        body: JSON.stringify({imageData: image}),
     });
 }

return(
 <div>
      <input
        onChange={handleImageChange}
        id="logo"
        type="file"
        multiple="false"
        accept="image/*"
      />
      <button onClick={onSaveHandler}>
        SAVE
      </button>
 </div>);

In Django (DRF):

class CustomerViewSet(viewsets.ModelViewSet):
      # override create method
      def create(self, request, *args, **kwargs):
        image_path = "whatever.jpg"
        print('save image on disk: ' + image_path)
        with open(image_path, "wb") as fh:
            fh.write(base64.b64decode(request.data.get("imageData")))
        return super().create(request)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM