Android uses Retrofit 2 to implement multiple file uploads

Android uses Retrofit 2 to implement multiple file uploads

Some time ago, I translated Future Studio's Retrofit2 tutorial, from which I also learned some usage methods of Retrofit2. If you are also planning to start learning it recently, you may want to refer to the Retrofit tutorial on my blog: Retrofit tutorial.

As a summary of the stage, this article will use the Flask framework in Python to implement the Android client multi-file upload function. If the reader has not used Flask in Python, it does not matter. You can only read the Android client part. After all, client engineers can also only use the API.

1. Experimental results

Android operation screenshots

The image received by the server

2. Server-side practice

The server is responsible for receiving and saving the images uploaded by the client and providing the ability to access the images. There are many technologies that can be used to implement the server. Python, as a language with powerful third-party libraries, has many web service frameworks, such as Flask, Django, etc. The author uses the Flask framework, which is a microframework and is very convenient for implementing small functions. The multi-file upload function implemented by the author has no more than 30 lines of program.

Let’s take a closer look below.

2.1 Environment Installation

The Python version I use is 3.4. You can go to Python 3.4 downloads to choose the version that suits your system. Please search for the complete Python installation tutorial yourself.

After Python is installed, you need to install the server-side program dependency library. Install via pip:

  1. pip install Flask
  2. pip install werkzeug

2.2 Program Implementation

First, we need to introduce the dependent library:

  1. from flask import Flask,request,send_from_directory,jsonify
  2. import os
  3. from werkzeug import secure_filename

This experiment requires uploading files. It is necessary to restrict the file type and file name of the uploaded file to prevent certain programs from running to damage the server. In addition, some illegal file names are as follows:

filename = "../../../../home/username/.bashrc"

If hackers can manipulate such files, it will be a fatal blow to the server system. Therefore, werkzeug provides secure_filename to verify the legality of the uploaded file name.

Determine whether the file suffix is ​​legal

  1. ALLOWED_EXTENSIONS= set ([ 'png' , 'jpg' , 'jpeg' , 'gif' ])
  2. def allowed_file(filename):
  3. return   '.'   in filename and filename.rsplit( '.' ,1)[1] in ALLOWED_EXTENSIONS

The function code for receiving uploaded files is as follows:

  1. @app.route( '/upload' ,methods=[ 'POST' ])
  2. def upload_file():
  3. if request.method == 'POST' :
  4. for k in request.files:
  5. file = request.files[k]
  6. image_urls = []
  7. if file and allowed_file(file.filename):
  8. filename=secure_filename(file.filename)
  9. file.save(os.path.join (app.config[ ' IMAGE_FOLDER' ],filename))
  10. image_urls.append( "images/%s" %filename)
  11. return jsonify({ "code" :1, "image_urls" :image_urls})

Flask supports HTTP request methods such as GET, POST, PUT, and DELETE, and uses decorators for modification, which is similar to the concept of annotations in Java. /upload is the relative address requested by the client, and the request method is limited to POST. According to the built-in object of request, you can access the files sent by the client, check the files and save them locally, where image_urls is the relative address array of the uploaded images. ***Return the address of the image to the client in json format.

The complete server-side code is as follows:

  1. from flask import Flask,request,send_from_directory,jsonify
  2. import os
  3. from werkzeug import secure_filename
  4.  
  5. app = Flask(__name__)
  6. app.config[ 'IMAGE_FOLDER' ] = os.path.abspath( '.' )+ '\\images\\'  
  7. ALLOWED_EXTENSIONS= set ([ 'png' , 'jpg' , 'jpeg' , 'gif' ])
  8.  
  9. def allowed_file(filename):
  10. return   '.'   in filename and filename.rsplit( '.' ,1)[1] in ALLOWED_EXTENSIONS
  11.  
  12. @app.route( '/upload' ,methods=[ 'POST' ])
  13. def upload_file():
  14. if request.method == 'POST' :
  15. for k in request.files:
  16. file = request.files[k]
  17. print(file)
  18. image_urls = []
  19. if file and allowed_file(file.filename):
  20. filename=secure_filename(file.filename)
  21. file.save(os.path.join (app.config[ ' IMAGE_FOLDER' ],filename))
  22. image_urls.append( "images/%s" %filename)
  23. return jsonify({ "code" :1, "image_urls" :image_urls})
  24.  
  25. #Allow file mapping access, otherwise only files in the static folder can be accessed by default
  26. @app.route( "/images/<imgname>" ,methods=[ 'GET' ])
  27. def images(imgname):
  28. return send_from_directory(app.config[ 'IMAGE_FOLDER' ],imgname)
  29.  
  30. if __name__ == "__main__" :
  31.  
  32. # Check if IMAGE_FOLDER exists
  33. if not os.path.exists(app.config[ 'IMAGE_FOLDER' ]):
  34. os.mkdir(app.config[ 'IMAGE_FOLDER' ])
  35. app.run( "192.168.1.102" ,debug= True )

Here is a little trick. After writing the server-side code, you can use Postman to test it. After the test is successful, you can develop the client program.

3. Client Development

Because it involves uploading files, I will take pictures as an example to upload experiments. In addition to Retrofit, picture uploading also requires selecting pictures. I recommend an image selection library ImagePicker that imitates WeChat.

3.1 Adding Dependency Libraries

Image loading library I like to use Glide

  1. compile 'com.squareup.retrofit2:retrofit:2.1.0'  
  2. compile 'com.squareup.retrofit2:converter-gson:2.1.0'  
  3. compile 'com.github.bumptech.glide:glide:3.7.0'  
  4. compile 'com.lzy.widget:imagepicker:0.4.1'  

3.2 Program Implementation

If you haven't come across Retrofit 2, you can learn more about it in my blog Retrofit tutorial.

Retrofit2 is a request library that supports RESTful API. In fact, it is just an encapsulation of the API request method. The real network request is issued by OkHttp.

Retrofit2 generally defines a ServiceGenerator class to dynamically generate Retrofit objects.

  1. public class ServiceGenerator {
  2. public   static final String API_BASE_URL = "http://192.168.1.102:5000/" ;
  3. private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
  4.   
  5. private static Retrofit.Builder builder =
  6. new Retrofit.Builder()
  7. .baseUrl(API_BASE_URL)
  8. .addConverterFactory(GsonConverterFactory. create ());
  9.   
  10. public   static <S> S createService(Class<S> serviceClass) {
  11. Retrofit retrofit = builder.client(httpClient.build()).build();
  12. return retrofit. create (serviceClass);
  13. }
  14. }

The specific API operations are performed by the FlaskClient interface.

  1. public interface FlaskClient {
  2. //Upload picture
  3. @Multipart
  4. @POST( "/upload" )
  5. Call<UploadResult> uploadMultipleFiles(@PartMap Map<String,RequestBody> files);
  6. }

To upload a file, you need to use the @Multipart keyword annotation. @POST indicates that the HTTP request method is POST, /upload is the relative address of the request server, uploadMultipleFiles is a custom method name, and the parameter is Map<String,RequestBody> files, which is a Map object composed of multiple files. @PartMap indicates that this is a multi-file upload. If a single file can use @Part MultipartBody.Part file, the return type of the method defaults to Response. Since we have developed the Server side, we know that the return data format of the Server side is Json, so we create a new UploadResut class for the return data format.

  1. public class UploadResult {
  2. public   int code; // 1
  3. public List<String> image_urls;
  4. }

The interface layout is shown in the figure:

Click the Upload button to perform the upload operation. The core method is:

  1. public void uploadFiles() {
  2. if(imagesList. size () == 0) {
  3. Toast.makeText(MainActivity.this, "Cannot not select a picture" , Toast.LENGTH_SHORT).show();
  4. return ;
  5. }
  6. Map<String, RequestBody> files = new HashMap<>();
  7. final FlaskClient service = ServiceGenerator.createService(FlaskClient.class);
  8. for ( int i = 0; i < imagesList. size (); i++) {
  9. File file = new File(imagesList.get(i).path);
  10. files.put( "file" + i + "\"; filename=\"" + file.getName(), RequestBody. create (MediaType.parse(imagesList.get(i).mimeType), file));
  11. }
  12. Call<UploadResult> call = service.uploadMultipleFiles(files);
  13. call.enqueue(new Callback<UploadResult>() {
  14. @Override
  15. public void onResponse(Call<UploadResult> call, Response<UploadResult> response) {
  16. if (response.isSuccessful() && response.body().code == 1) {
  17. Toast.makeText(MainActivity.this, "Upload successful" , Toast.LENGTH_SHORT).show();
  18. Log.i( "orzangleli" , "---------------------Upload successful-----------------------" );
  19. Log.i( "orzangleli" , "The base address is:" + ServiceGenerator.API_BASE_URL);
  20. Log.i( "orzangleli" , "The relative address of the image is: " + listToString(response.body().image_urls, ',' ));
  21. Log.i( "orzangleli" , "---------------------END-----------------------" );
  22. }
  23. }
  24. @Override
  25. public void onFailure(Call<UploadResult> call, Throwable t) {
  26. Toast.makeText(MainActivity.this, "Upload failed" , Toast.LENGTH_SHORT).show();
  27. }
  28. });
  29. }

The parameters for constructing the method for uploading multiple files are critical. MediaType.parse(imagesList.get(i).mimeType) obtains the mimeType of the image. If an error is specified, the upload may fail.

  1. Map<String, RequestBody> files = new HashMap<>();
  2. final FlaskClient service = ServiceGenerator.createService(FlaskClient.class);
  3. for ( int i = 0; i < imagesList. size (); i++) {
  4. File file = new File(imagesList.get(i).path);
  5. files.put( "file" + i + "\"; filename=\"" + file.getName(), RequestBody. create (MediaType.parse(imagesList.get(i).mimeType), file));
  6. }

The second parameter of the onResponse method of the anonymous callback class that integrates the Callback interface is the server response. By accessing the body() method, an object of type UploadResult is returned. Then, the uploaded image can be accessed by combining ServiceGenerator.API_BASE_URL and each item in response.body().image_urls.

4. Project address

Both the client and server of this project are open source, welcome all bosses to Star.

Client address: RetrofitMultiFilesUploadClient

Server Address: MultiFileUploadServer

Original link: http://www.orzangleli.com/2017/04/03/2017-04-03_Android%20%E7%94%A8%20Retrofit%202%20%E5%AE%9E%E7%8E%B0%E5%A4%9A%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E5%AE%9E%E6%88%98/

<<:  Android digital jumping TextView implementation

>>:  Changes to device identifiers in Android O

Recommend

Brand Doctor - Little Red Book full-chain marketing tips

The most comprehensive information about Xiaohongs...

Can't breathe when wearing a mask in summer? Try breathing like this

When the epidemic and high temperature overlap, c...

How can the e-commerce industry effectively recall lost users?

When recalling users , you first need to screen o...

Xigua Video product analysis report!

As a product of Toutiao, Xigua Video has been aro...

Luke Media’s 4th live streaming offline practical course

Introduction to the resources of Luke Media's...

Traffic pool thinking and rapid creation of traffic pool!

Luckin Coffee co-founder and CMO Yang Fei shared ...

How much does it cost to make a small program in Guangzhou?

It is estimated that many people do not know much...