0

I need to upload an image with caption and username to API that is built using Django. Create Post view in Django is marked with @permission_classes((IsAuthenticated,)). This is the code:

@permission_classes((IsAuthenticated,))
class PostCreateAPIView(CreateAPIView):
    serializer_class = PostSerializer

    def get_queryset(self):
        return Post.objects.all()

Serializer:

class PostSerializer(ModelSerializer):
    class Meta:
        model = Post
        fields = ('author', 'description', 'image', 'created_at')

I did some research and found that since only authenticated users can post image I need to somehow use the token which users receive on login.

I am getting the user token when I login and have been able to save it locally using hive. However I have no idea what to do next.

static Future<dynamic> loginUser(String username, String password) async {
    final response = await http.post("$apiURL/en/api/users/login/", body: {
      "username": username,
      "password": password,
    });

    return response?.body;
  }

This is my login code and it returns json format with username, user_id and token. Smth like this:

{
    "token": "dc9e0de8fa2eaa917657e810db06aad2458e4f65",
    "user_id": 4,
    "username": "maria"
}
Davrick
  • 301
  • 1
  • 4
  • 13
  • For sending files using api you can follow this : https://stackoverflow.com/a/51162343/4723045 and for saving user token after login use shared preferences as using Hive will be an overkill for saving just Token. You can head here for saving and reading data from shared preferences https://stackoverflow.com/a/54031842/4723045 – Naveen Rao Mar 08 '21 at 15:24
  • Thank you. The first link was useful. However they did not include Authorization with token ( – Davrick Mar 08 '21 at 15:34
  • Please mark my answer as selected answer if that helped you – Naveen Rao Mar 08 '21 at 16:53
  • Thank you. It is working I guess. Now when I upload image it is returning html tags saying `413 Request Entity Too Large`. I think I am gonna have to compress the image before sending. – Davrick Mar 08 '21 at 21:03

2 Answers2

2

Merging my suggestion given in comments.

Write this to add headers with authentication token and files both at the same time :

upload(File imageFile, String token) async {    
      // open a bytestream
      var stream = new http.ByteStream(DelegatingStream.typed(imageFile.openRead()));
      // get file length
      var length = await imageFile.length();

      // string to uri
      var uri = Uri.parse("http://ip:8082/composer/predict");

      // create multipart request
      var request = new http.MultipartRequest("POST", uri);

      // add headers with Auth token
      Map<String, String> headers = { "Authorization": "Token $token"};

      request.headers.addAll(headers);

      // multipart that takes file
      var multipartFile = new http.MultipartFile('file', stream, length,
          filename: basename(imageFile.path));

      // add file to multipart
      request.files.add(multipartFile);

      // send
      var response = await request.send();
      print(response.statusCode);

      // listen for response
      response.stream.transform(utf8.decoder).listen((value) {
        print(value);
      });
    }
Naveen Rao
  • 712
  • 6
  • 10
  • I am getting 401 error saying `{"detail":"Authentication credentials were not provided."}`. Any ideas on how to fix that ? I think the token was passed not correctly. – Davrick Mar 08 '21 at 22:51
  • @Davrick please share your code. How are you adding token to api and file ? – Naveen Rao Mar 09 '21 at 11:06
  • `var uri = Uri.parse("$baseURL/posts/create/"); var request = new http.MultipartRequest("POST", uri); Map headers = { "Authorization": "Token ${box.get('token')}" }; request.headers.addAll(headers); var multipartFile = new http.MultipartFile('file', stream, length, filename: path.basename(_image.path)); request.files.add(multipartFile); var response = await request.send(); print(response.statusCode); response.stream.transform(utf8.decoder).listen((value) { print(value); }); ` – Davrick Mar 09 '21 at 19:59
  • Hi everyone, token is working but I got this little issue now. When I am sending the above request. I am getting response saying `"image":["No file was submitted."]` – Davrick Mar 11 '21 at 12:08
  • I guess you need to check with your backend developer for this response. Either image key with which you're sending file is not correct or there can be some issue on the backend side. – Naveen Rao Mar 12 '21 at 06:31
  • this worked like a charm - I just replaced `"Authorization": "Token $token"` `"Authorization": "Bearer $token"` for my `token` – Aristidios Feb 09 '22 at 14:33
  • just maybe update answer slightly as `'typed' is deprecated and shouldn't be used` – Aristidios Feb 09 '22 at 14:34
0

What you probably want to do is to pass the token inside the request header. Something like this should work:

  static Future<dynamic> loginUser(String username, String password) async {
    final response = await http.post(
      "$apiURL/en/api/users/login/",
      body: {
        "username": username,
        "password": password,
      },
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'Authorization': 'Token $token',
      },
    );

    return response?.body;
  }

Note that the headers contains a field named Authorization, this is the place where you pass your the token. The keyword Token inside the string containing the token specifies the kind of authentication you are using. If it doesn't work, maybe you are using another kind of Authorization.

I recommend you take a look at https://www.django-rest-framework.org/api-guide/authentication/

Novak
  • 106
  • 6
  • But I am having issue with posting image not with login. Will it work the same if I post image ? – Davrick Mar 08 '21 at 15:21
  • oh, sorry for the misunderstanding. I think the solution given by @NaveenRao will work as expected. If not, let me know and I will try to help you with another solution – Novak Mar 08 '21 at 16:29