I translated a Retrofit2 tutorial from Future Studio a while ago and learned a few things about how to use Retrofit2. If you’re going to start learning about Retrofit2 in the near Future, I have a Retrofit tutorial on my blog that you might want to check out: Retrofit Tutorial.

As a brief summary, this article will use the Flask framework in Python to implement the multi-file upload function on Android. It doesn’t matter if you haven’t used Flask in Python, you can just look at the Android client section, after all, client engineers can just use the API.

1. Experimental effect

  • Screenshot of operation on Android

  • The image received by the Server

2. Server deployment

The Server is responsible for receiving and saving images from the client and providing the ability to access images. There are many technologies that can be implemented on the Server. Python, as a language with powerful third-party libraries, has many Web service frameworks, such as Flask,Django, etc. The author adopts Flask framework, Flask is a micro framework, it is very convenient to realize small functions, the author realized the multi-file upload function, the program is not more than 30 lines.

Let’s see.

2.1 Environment Installation

The Python version I’m using is 3.4, so you can go to Python3.4 and download the appropriate version for your system. Search for the complete Python installation tutorial.

After the Python installation is complete, you need to install the Server program dependency library. Install via PIP:

pip install Flask
pip install werkzeugCopy the code

2.2 Program Implementation

First we introduce the dependency library:

from flask import Flask,request,send_from_directory,jsonify
import os
from werkzeug import secure_filenameCopy the code

In this experiment, files need to be uploaded. The file type and filename of the uploaded file need to be restricted to prevent some programs that damage the server from running. In addition, some illegal file names such as filename = “.. /.. /.. /.. /home/username/.bashrc” If hackers could manipulate such files, it would be a fatal blow to the server system. So Werkzeug provides secure_filename to verify the filename of the uploaded file.

Determine whether the file suffix is valid

ALLOWED_EXTENSIONS=set(['png'.'jpg'.'jpeg'.'gif'])
def allowed_file(filename):
    return '. ' in filename and filename.rsplit('. '.1) [1] in ALLOWED_EXTENSIONSCopy the code

The function code for receiving the uploaded file is as follows:

@app.route('/upload',methods=['POST'])
def upload_file(a):
    if request.method=='POST':
        for k in request.files:
            file = request.files[k]
            image_urls = []
            if file and allowed_file(file.filename):
                filename=secure_filename(file.filename)
                file.save(os.path.join(app.config['IMAGE_FOLDER'],filename))
                image_urls.append("images/%s"%filename)
        return jsonify({"code":1."image_urls":image_urls})Copy the code

Flask supports HTTP requests such as GET,POST,PUT, and DELETE. It is decorated with decorators, similar to annotations in Java. /upload is the relative address of client requests, and the request mode is limited to POST. According to the Request built-in object, you can access the file sent by the client, check it and save it locally, where image_urls is the relative address array of the uploaded image. Finally, the address of the image is returned to the client in JSON format.

The complete server-side code is as follows:

from flask import Flask,request,send_from_directory,jsonify
import os
from werkzeug import secure_filename

app = Flask(__name__)
app.config['IMAGE_FOLDER'] = os.path.abspath('. ') +'\\images\\'
ALLOWED_EXTENSIONS=set(['png'.'jpg'.'jpeg'.'gif'])

def allowed_file(filename):
    return '. ' in filename and filename.rsplit('. '.1) [1] in ALLOWED_EXTENSIONS

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

Otherwise, only files in the static folder can be accessed by default
@app.route("/images/<imgname>",methods=['GET'])
def images(imgname):
    return send_from_directory(app.config['IMAGE_FOLDER'],imgname)

if __name__ == "__main__":
    # Check if the IMAGE_FOLDER exists
    if not os.path.exists(app.config['IMAGE_FOLDER']):
        os.mkdir(app.config['IMAGE_FOLDER'])
    app.run("192.168.1.102",debug=True)Copy the code

Here is a tip. After writing the Server 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 the uploading of files, the author here takes pictures as an example for uploading experiments. Besides Retrofit, image uploading also needs to select pictures. The author here recommends ImagePicker, an image selection library imitating wechat.

3.1 Adding a dependency Library

Picture loading library I like to use Glide

compile 'com. Squareup. Retrofit2: retrofit: 2.1.0'
compile 'com. Squareup. Retrofit2: converter - gson: 2.1.0'
compile 'com. Making. Bumptech. Glide: glide: 3.7.0'
compile 'com. Lzy. Widget: imagepicker: 0.4.1'Copy the code

3.2 Program Implementation

If you haven’t touched Retrofit 2, check out Retrofit tutorials on my blog.

Retrofit2 is a library of requests that support RESTful apis. It’s really just a wrapper around the WAY API requests are made. The real web requests are made by OkHttp.

Retrofit2 typically defines a ServiceGenerator class for dynamically generating Retrofit objects.

public class ServiceGenerator {
    public static final String API_BASE_URL = "http://192.168.1.102:5000/";
    private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();

    private static Retrofit.Builder builder =
            new Retrofit.Builder()
                    .baseUrl(API_BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create());

    public static <S> S createService(Class<S> serviceClass) {
        Retrofit retrofit = builder.client(httpClient.build()).build();
        returnretrofit.create(serviceClass); }}Copy the code

Specific API operations are operated by the FlaskClient interface,

public interface FlaskClient {
    // Upload the image
    @Multipart
    @POST("/upload")
    Call<UploadResult> uploadMultipleFiles( @PartMap Map
       
         files)
       ,requestbody>;
}Copy the code

@post indicates that the HTTP request mode is POST. /upload indicates the relative address of the request server. UploadMultipleFiles is a custom method name. RequestBody> files = Map

files = multiple files, @partmap = multiple file upload If a single file can use @part multipartBody. Part file, the return type of the method is Response by default. Since we have developed the Server side, we know that the return data format of the Server side is Json. So let’s create a new UploadResut class for the returned data format.
,requestbody>

public class UploadResult {
    public int code;    / / 1
    public List<String> image_urls;
}Copy the code

The interface layout is as follows:

Click Upload button to perform Upload operation, and the core method is as follows:

public void uploadFiles() {
    if(imagesList.size() == 0) {
        Toast.makeText(MainActivity.this, "Can't not select the image", Toast.LENGTH_SHORT).show();
        return;
    }
    Map<String, RequestBody> files = new HashMap<>();
    final FlaskClient service = ServiceGenerator.createService(FlaskClient.class);
    for (int i = 0; i < imagesList.size(); i++) {
        File file = new File(imagesList.get(i).path);
        files.put("file" + i + "\"; filename=\"" + file.getName(), RequestBody.create(MediaType.parse(imagesList.get(i).mimeType), file));
    }
    Call<UploadResult> call = service.uploadMultipleFiles(files);
    call.enqueue(new Callback<UploadResult>() {
        @Override
        public void onResponse(Call<UploadResult> call, Response<UploadResult> response) {
            if (response.isSuccessful() && response.body().code == 1) {
                Toast.makeText(MainActivity.this, "Upload successful", Toast.LENGTH_SHORT).show();
                Log.i("orzangleli"."-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - upload success -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
                Log.i("orzangleli"."The basic address is:" + ServiceGenerator.API_BASE_URL);
                Log.i("orzangleli"."The relative address of picture is:" + listToString(response.body().image_urls,', '));
                Log.i("orzangleli"."---------------------END-----------------------");
            }
        }
        @Override
        public void onFailure(Call<UploadResult> call, Throwable t) {
            Toast.makeText(MainActivity.this, "Upload failed", Toast.LENGTH_SHORT).show(); }}); }Copy the code

MediaType. Parse (imageslist.get (I).mimeType) retrieves the mimeType of the image. If the parameter is specified incorrectly, the upload may fail.

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

The second argument to the onResponse method of the anonymous Callback class that integrates the Callback excuse is the server response, which returns an object of type UploadResult by calling the body() method, You can then access the uploaded image by combining each item in serviceGenerator.api_base_URL and Response.body ().image_urls.

4. Project address

The Client and Server end of this project are open source, welcome all bosses Star. The Client address: RetrofitMultiFilesUploadClient Server address: MultiFileUploadServer