There have been numerous articles on Retrofit2 on the Web, but they tend to be more descriptive than practical, partly because the free apis on the Web have limited data formats and access modes (usually Get mode only). Another is that not every developer can write a server-side interface. As a result, the effects of certain parameters were not intuitive when learning Retrofit2, so I tried to build a local server using Nodejs, which provided several interfaces for Get and Post mode access calls, file upload and file download functions. The returned data is formatted as Json objects and Json arrays, and the required parameter formats can be customized by the user
This article won’t go too far into the use of Retrofit2, but will focus on building a server-side interface and how Retrofit2 interacts with the server
First, the server
The server interface uses Nodejs, the IDE is WebStorm, Nodejs version is 10.2.0
Open WebStorm, select New Project, selectNode.js Express AppTo create a project
The upload folder is used to store the files sent from the client. The resultjson. js file is used to unify the data format returned by the server. The api.js file is used to hold the written interface and start the server, and it is the api.js file that we need to focus on
1.1, resultJson. Js
Here is the resultjson. js file, which contains all the code shown below
/** * if a normal result is returned *@param res
* @param data* /
exports.onSuccess = function (res, data) {
var result = {};
result.code = 1;
result.msg = 'success';
result.data = data;
res.json(result);
};
/** * when an error occurs *@param res
* @param code
* @param msg* /
exports.onError = function (res, code, msg) {
var error = {};
error.code = code;
error.msg = msg;
res.json(error);
};
/** * No data is recorded *@param res* /
exports.onNoRecord = function (res) {
exports.onError(res, 1000.'No record');
};
/** * Parameter error *@param res* /
exports.onParamsError = function (res) {
exports.onError(res, 1001.'Parameter error');
};
/** * System error *@param res* /
exports.onSystemError = function (res) {
exports.onError(res, 1002.'System error');
};
Copy the code
Resultjson. js encapsulates all possible results of network requests and unify the data format returned by the server. When a normal result is returned, the onSuccess method is called, and the returned data format is similar to the following. The return code is fixed as “1”, and the returned message MSG is fixed as “success”, and data contains the actual data to be returned
{"code":1."msg":"success"."data": {"name":"leavesC"."mobile":123456}}
Copy the code
When the argument passed to the server is wrong, the onParamsError method is called and the data returned is in the format shown below
{"code":1001."msg":"Parameter error"}
Copy the code
Other abnormal cases return data in the same format, containing only different return codes and return information values
1.2, API. Js
The api.js file contains all the interfaces. Here is a Get interface, and others will be introduced as they are used
In this case, the require function is used to load the required module, similar to loading the required dependent libraries in Java. App. The get () show that the interface support is get pattern request, access interface path suffix is: “/ get/get string”, the complete access path is: http://localhost:1995/Get/getString
The req parameter contains the request parameters brought by the client, the res parameter is used to write the data to be returned to the client, and app.listen(1995) is used to start the server and specify port 1995 for listening
When the client accesses the interface, the interface prints out all the request parameters and headers brought by the client, as well as the actual generated access links
This completes a simple Get interface
// the require function is used to load the required module
var express = require('express');
var bodyParser = require('body-parser');
var multiparty = require('multiparty');
var resultJson = require('.. /routes/resultJson');
var app = express();
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.get('/Get/getString'.function (req, res) {
// Request parameters
var query = req.query;
for (var key in query) {
console.log("Key is:", key, " , value is: ", query[key]);
}
/ / request header
var headers = req.headers;
for (var key in headers) {
console.log(Header key is:, key, " , value is: ", headers[key]);
}
/ / links
console.log("Url:", req.url);
// If the request header has a key value of "userName", if the value is not "leavesC", the request parameter is considered wrong
// If there is no request header with key value "userName", the request is not affected
// Note that the key value in the request header is set to lowercase
var userName = headers['username'];
if(userName && userName ! = ='leavesC') {
return resultJson.onParamsError(res);
}
var data = {};
data.name = 'leavesC';
data.mobile = 123456; resultJson.onSuccess(res, data); }); ...// Start the server and listen on the specified port 1995
app.listen(1995);
Copy the code
Second, the client
The IDE used by the client is IntelliJ IDEA. Gradle is used to build the project, which is basically consistent with Android Studio
Introduced support for Retrofit2 and Converter-Gson
implementation 'com. Squareup. Retrofit2: retrofit: 2.4.0'
implementation 'com. Squareup. Retrofit2: converter - gson: 2.4.0'
Copy the code
A Get request
Since I set up the server locally, the baseUrl used to build Retrofit should point to a local IP address
/ * * * the author: chenZY * time: 2018/5/29 18:53 * description: https://github.com/leavesC * /
public class HttpConfig {
public static final String BASE_URL = "http://localhost:1995/";
}
Copy the code
The new GetService interface is used to declare the methods to access the Get interface. Each method contains different parameter values. You can easily distinguish the different methods according to the log information printed by the server
/ * * * the author: chenZY * time: 2018/5/29 18:54 * description: https://github.com/leavesC * /
public interface GetService {
// Get request with no parameters
@GET("Get/getString")
Call<ResponseBody> getNormal(a);
// Get request with request parameters
@GET("Get/getString")
Call<ResponseBody> getWithQuery(@Query("name") String name, @Query("age") int age);
// Get request with request parameters
@GET("Get/getString")
Call<ResponseBody> getWithMap(@QueryMap Map<String, String> map);
// Get requests with request parameters and fixed headers
@GET("Get/getString")
@Headers({"userName:leavesC"})
Call<ResponseBody> getWithQueryAndHeaders(@Query("name") String name, @Query("age") int age);
// Get requests with variable request parameters and headers
@GET("Get/getString")
Call<ResponseBody> getWithQueryAndHeader(@Header("userName") String userName, @Query("name") String name, @Query("age") int age);
// A Get request with the request value as part of the link
@GET("Get/getString/{id}")
Call<ResponseBody> getWithPath(@Path("id") int id);
// Get requests with the request value as part of the link, and use Gson Converter
@GET("Get/getUser/{startId}/{number}")
Call<ListResponse<User>> getWithGsonConverter(@Path("startId") int startId, @Path("number") int number);
}
Copy the code
2.1. Without any parameters
Here’s a look at the request without any custom parameters and headers
//Get requests do not take any custom parameters or headers. The access link is: /Get/getString
private static void getNormal(a) {
GetService getService = buildRetrofit().create(GetService.class);
getService.getNormal().enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
/ / return data: {" code ": 1," MSG ":" success ", "data" : {" name ":" leavesC ", "mobile" : 123456}}
System.out.println("onResponse body: " + response.body().string());
} catch(IOException e) { e.printStackTrace(); }}else {
System.out.println("onResponse code: "+ response.code()); }}@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
System.out.println("onFailure: "+ t.getMessage()); }}); }Copy the code
Log information displayed on the server is as follows
Header information Key is: host, value is: localhost:1995Header information Key is: connection, value is: keep-alive header information Key is: accept-encoding, value is: gzip header information Key is: user-agent , value is: okhttp/3.10. 0Url: / Get/Get stringCopy the code
The data obtained by the client is shown below
{"code":1."msg":"success"."data": {"name":"leavesC"."mobile":123456}}
Copy the code
2.2. Bring the request parameters
If you annotate the request method with @query and the corresponding request parameter, the request parameter will be the suffix of the access link
//Get requests will take the request parameters, which will be the suffix of the link, resulting in the link: /Get/getString? name=leavesC&age=24
private static void getWithQuery(a) {
GetService getService = buildRetrofit().create(GetService.class);
getService.getWithQuery("leavesC".24).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
/ / the data returned is: {" code ": 1," MSG ":" success ", "data" : {" name ":" leavesC ", "mobile" : 123456}}
System.out.println("onResponse body: " + response.body().string());
} catch(IOException e) { e.printStackTrace(); }}else {
System.out.println("onResponse code: "+ response.code()); }}@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
System.out.println("onFailure: "+ t.getMessage()); }}); }Copy the code
Log information displayed on the server is as follows
Parameter Key is: name, value is: leavesC Parameter Key is: age, value is: 24 Header information Key is: host, value is: Localhost :1995 Header information Key is: connection, value is: keep-alive Header information Key is: accept-encoding, value is: Gzip header information Key is: user-agent, value is: okhttp/3.10.0 Url: /Get/getString? name=leavesC&age=24Copy the code
The server obtains the parameter information brought by the client through req.query, the server can obtain the corresponding data from the database according to the parameter information, so as to realize the conditional index data
The getWithMap() method does the same thing as getWithQuery() and won’t be described here
2.3, with a fixed request header
The getWithQueryAndHeaders() method is a Get request that carries the request parameters and a fixed request header
//Get requests with parameters and request headers. Parameters are suffixes for links. The resulting link is: /Get/getString? name=leavesC&age=24
// The key of the Header is userName, and the value is leavesC
private static void getWithQueryAndHeaders(a) {
GetService getService = buildRetrofit().create(GetService.class);
getService.getWithQueryAndHeaders("leavesC".24).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
/ / the data returned is: {" code ": 1," MSG ":" success ", "data" : {" name ":" leavesC ", "mobile" : 123456}}
System.out.println("onResponse body: " + response.body().string());
} catch(IOException e) { e.printStackTrace(); }}else {
System.out.println("onResponse code: "+ response.code()); }}@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
System.out.println("onFailure: "+ t.getMessage()); }}); }Copy the code
At this time, the log information printed by the server is as follows. You can see that the header information contains username, and the value is the same as that stated in the annotation
Parameter Key is: name, value is: leavesC Parameter Key is: age, value is: 24 Header information Key is: username, value is: LeavesC2 Header information Key is: host, value is: localhost:1995 Header information Key is: Connection, value is: keep-alive header information Key is: Accept-encoding, value is: gzip header key is: user-agent, value is: okhttp/3.10.0 Url: /Get/getString? name=leavesC&age=24Copy the code
The header information can be used to authenticate the source of the access, that is, to authenticate the identity of the client
On the server side, I judged the value of the header information whose key value is userName. If the client contains the header information whose key value is userName, but its value is not leavesC, the returned Json data will indicate parameter error
Modifies the value of the header information carried by the getWithQueryAndHeaders() method
/** ** Author: chenZY * Time: 2018/5/29 18:54 * Description: */
public interface GetService {
// Get requests with request parameters and fixed headers
@GET("Get/getString")
@Headers({"userName:leavesC_2"})
Call<ResponseBody> getWithQueryAndHeaders(@Query("name") String name, @Query("age") int age);
}
Copy the code
The data returned by the server will be
{"code":1001."msg":"Parameter error"}
Copy the code
2.4. Request headers with non-fixed values
The @header annotation used to mark non-fixed value request headers is applied to method parameters, enabling dynamic assignment of request headers
//Get request headers with parameters and non-fixed values. The parameters will be the suffix of the link. The generated link is: /Get/getString? name=leavesC&age=24
// The key of the Header is userName and the value is Hi
private static void getWithQueryAndHeader(a) {
GetService getService = buildRetrofit().create(GetService.class);
getService.getWithQueryAndHeader("Hi"."leavesC".24).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
/ / the data returned is: {" code ": 1," MSG ":" success ", "data" : {" name ":" leavesC ", "mobile" : 123456}}
System.out.println("onResponse body: " + response.body().string());
} catch(IOException e) { e.printStackTrace(); }}else {
System.out.println("onResponse code: "+ response.code()); }}@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
System.out.println("onFailure: "+ t.getMessage()); }}); }Copy the code
The server prints the following log, which is not much different from the @headers annotation except that one value is fixed and the other is assigned dynamically at run time
Key is: name, value is: leavesC Key is: age, value is:24Header key is: username, value is: Hi Header key is: host, value is: localhost:1995Header information Key is: connection, value is: keep-alive header information Key is: accept-encoding, value is: gzip header information Key is: user-agent , value is: okhttp/3.10. 0Url: / Get/Get string? name=leavesC&age=24
Copy the code
2.5. Specify an access path
There is also a way to add access parameters to the link as an actual part of the link
The corresponding client method is
@GET("Get/getString/{id}")
Call<ResponseBody> getWithPath(@Path("id") int id);
Copy the code
In this case, you need to write another Get interface on the server. The :id in /Get/getString/:id indicates that the client can access the callback function of the interface only when the client explicitly carries the parameter value (no Key is required)
app.get('/Get/getString/:id'.function (req, res) {
// Request parameters
var query = req.query;
for (var key in query) {
console.log("Key is:", key, " , value is: ", query[key]);
}
/ / request header
var headers = req.headers;
for (var key in headers) {
console.log(Header key is:, key, " , value is: ", headers[key]);
}
/ / links
console.log("Url:", req.url);
var id = req.params.id;
if (id <= 0) {
resultJson.onParamsError(res);
} else {
var data = {};
data.name = 'leavesC_' + id;
data.mobile = 123456; resultJson.onSuccess(res, data); }});Copy the code
The client accesses the interface
// The generated link is: /Get/getString/22
private static void getWithPath(a) {
GetService getService = buildRetrofit().create(GetService.class);
getService.getWithPath(22).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
/ / return data: {" code ": 1," MSG ":" success ", "data" : {" name ":" leavesC_22 ", "mobile" : 123456}}
System.out.println("onResponse body: " + response.body().string());
} catch(IOException e) { e.printStackTrace(); }}else {
System.out.println("onResponse code: "+ response.code()); }}@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
System.out.println("onFailure: "+ t.getMessage()); }}); }Copy the code
Log information displayed on the server is as follows
Header information Key is: host, value is: localhost:1995Header information Key is: connection, value is: keep-alive header information Key is: accept-encoding, value is: gzip header information Key is: user-agent , value is: okhttp/3.10. 0Url: / Get/Get string22
Copy the code
2.6. Get the Json array
The previous requests are all Json objects. Here we write a return data format of the Josn array interface. Each Json object corresponds to the following Java Bean
/** ** Author: chenZY * Time: 2018/5/26 15:13 * Description: */
public class User {
private String name;
private String mobile;
public User(String name, String mobile) {
this.name = name;
this.mobile = mobile; }...}Copy the code
The server interface is used to obtain information about the number user whose ID starts from startId
app.get('/Get/getUser/:startId/:number'.function (req, res) {
// Request parameters
var query = req.query;
for (var key in query) {
console.log("Key is:", key, " , value is: ", query[key]);
}
/ / request header
var headers = req.headers;
for (var key in headers) {
console.log(Header key is:, key, " , value is: ", headers[key]);
}
/ / links
console.log("Url:", req.url);
// In order to prevent the parameters brought by the client from being non-numeric types, we need to check their types here
var startId = parseInt(req.params.startId);
var number = parseInt(req.params.number);
console.log("startId: ", startId);
console.log("number: ", number);
if (!isNaN(startId) && !isNaN(number) && startId > 0 && number > 0) {
var items = [];
for (var index = 0; index < number; index++) {
var item = {};
item.name = 'leavesC_' + (startId + index);
item.mobile = 123456;
items.push(item);
}
resultJson.onSuccess(res, items);
} else{ resultJson.onParamsError(res); }});Copy the code
The client uses Converter-gson to automatically parse the Json array returned by the server. Since the resultjson. js file unified the data format returned by the server, generics can be used for encapsulation in order to avoid writing code and MSG parameters every time
/** * Author: chenZY * Time: 2018/5/26 15:10 * Description: */
public class Response {
private int code;
privateString msg; ...}Copy the code
If the data returned by the server is a Json object, the EntityResponse is passed in via generics to the actual Java Bean
/** ** Author: chenZY * Time: 2018/5/26 15:11 * Description: */
public class EntityResponse<T> extends Response {
private T data;
public T getData(a) {
return data;
}
public void setData(T data) {
this.data = data; }}Copy the code
If the data returned by the server is a Json array, the actual Java Bean is passed in via generics, using ListResponse
/** ** Author: chenZY * Time: 2018/5/26 15:12 * Description: */
public class ListResponse<T> extends Response {
private List<T> data;
public List<T> getData(a) {
return data;
}
public void setData(List<T> data) {
this.data = data; }}Copy the code
The data contained in the List can now be retrieved directly from the callback function
private static void getWithGsonConverter(a) {
GetService getService = buildRetrofit().create(GetService.class);
getService.getWithGsonConverter(24.4).enqueue(new Callback<ListResponse<User>>() {
@Override
public void onResponse(Call<ListResponse<User>> call, Response<ListResponse<User>> response) {
if (response.isSuccessful()) {
List<User> userList = response.body().getData();
if (userList == null) {
System.out.println("onResponse: userList == null");
} else {
for (User user : userList) {
System.out.println("onResponse: "+ user); }}}else {
System.out.println("onResponse code: "+ response.code()); }}@Override
public void onFailure(Call<ListResponse<User>> call, Throwable t) {
System.out.println("onFailure: "+ t.getMessage()); }}); }Copy the code
Logs displayed on the client are as follows
onResponse: User{name='leavesC_24', mobile='123456'}
onResponse: User{name='leavesC_25', mobile='123456'}
onResponse: User{name='leavesC_26', mobile='123456'}
onResponse: User{name='leavesC_27', mobile='123456'}
Copy the code
A Post request
The server Post interface is similar to the Get interface. The main difference lies in the method of obtaining the parameters of the client Post interface
app.post('/Post/postUser'.function (req, res) {
var body = req.body;
for (var key in body) {
console.log("Body key is:", key, " , value is: ", body[key]);
}
/ / request header
var headers = req.headers;
for (var key in headers) {
console.log("Headers header key is:", key, " , value is: ", headers[key]);
}
/ / links
console.log("Url:", req.url);
var data = {};
data.name = 'leavesC';
data.mobile = 123456;
resultJson.onSuccess(res, data);
});
Copy the code
The PostService interface created by the client is used to declare the methods for accessing the Post interface. Each method contains different parameter values. The differences between the methods are identified according to the logs printed by the server
The @formurlencoded annotation indicates that the request header is a Form, corresponding to the content-Type request header that the client accesses the interface
/ * * * the author: chenZY * time: 2018/5/29 18:54 * description: https://github.com/leavesC * /
public interface PostService {
@FormUrlEncoded
@POST("Post/postUser")
Call<ResponseBody> postWithField(@Field("name") String name, @Field("mobile") String mobile);
@FormUrlEncoded
@POST("Post/postUser")
Call<ResponseBody> postWithFieldMap(@FieldMap Map<String, String> map);
@POST("Post/postUser")
Call<ResponseBody> postWithBody(@Body User user);
}
Copy the code
private static void postWithField(a) {
PostService postService = buildRetrofit().create(PostService.class);
postService.postWithField("czy"."123456").enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
/ / return data: {" code ": 1," MSG ":" success ", "data" : {" name ":" leavesC ", "mobile" : 123456}}
System.out.println("onResponse body: " + response.body().string());
} catch(IOException e) { e.printStackTrace(); }}else {
System.out.println("onResponse code: "+ response.code()); }}@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
System.out.println("onFailure: "+ t.getMessage()); }}); }Copy the code
The server prints the log as shown below, and you can see that the client carries past parameter values. In addition, the header “Content-Type” is the @formurlencoded annotation for the client interface method
Body parameter Key is: name, value is: CZY Body parameter key is: mobile, value is: czy123456Headers header key is: Content-type, value is: Application/X-www-form-urlencoded Headers header key is: Content-type, value is: Application/X-www-form-urlencoded Headers header key is: Content-type, value is: Application/X-www-form-urlencoded Headers header key is: content-length , value is:22Headers header information Key is: host, value is: localhost:1995Headers header information Key is: connection, value is: keep-alive Headers header information Key is: accept-encoding, value is: Gzip Headers header Information Key is: user-agent, value is: okhttp/3.10. 0Url: / Post/postUserCopy the code
Pass parameters via @FieldMap and @body annotations in the same way as @Field, Retrofit iterates through all the fields contained in the parameters to generate the parameters to pass, which I won’t go into here
Upload a file
Upload files with parameters
Here to simulate the client to upload a picture to the server, with parameter values
app.post('/uploadPhoto'.function (req, res) {
var body = req.body;
for (var key in body) {
console.log("Body key is:", key, " , value is: ", body[key]);
}
/ / request header
var headers = req.headers;
for (var key in headers) {
console.log("Headers header key is:", key, " , value is: ", headers[key]);
}
/ / links
console.log("Url:", req.url);
// Generate the multiparty object and configure the upload destination path
var form = new multiparty.Form({uploadDir: '.. /public/upload/'});
// Fields contains the parameter values passed in
//files represents the file object uploaded to the server
// The file sent from the client will be automatically saved to the specified folder in the background, and the processing result will be notified through the callback function
form.parse(req, function (err, fields, files) {
if (err) {
resultJson.onSystemError(res);
} else {
console.log("fields : ", fields);
console.log("files : ", files);
var filesContent = files['photo'] [0];
vardata = {}; data.filePath = filesContent.path; resultJson.onSuccess(res, data); }}); });Copy the code
The @multipart annotation indicates that the request body is a Form that supports file uploading. It corresponds to the request header whose key value is “Content-Type” when the client accesses the interface
In addition, three @Part annotations are used in the method parameters, the first to annotate the file object to be uploaded, and the remaining two to indicate the request parameters to be carried along with the uploaded file
/** * Author: chenZY * Time: 2018/5/29 18:55 * Description: */
public interface UploadService {
@Multipart
@POST("uploadPhoto")
Call<ResponseBody> uploadPhoto(@Part MultipartBody.Part photo, @Part("userName") RequestBody username, @Part("password") RequestBody password);
}
Copy the code
The images are placed under the resources folder of the project
private static void uploadPhoto(a) {
UploadService uploadService = buildRetrofit().create(UploadService.class);
File file = new File(".. \\JavaRetrofit\\src\\main\\resources\\images\\lufei.jpg");
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
/ / set the Content - Disposition: the form - data; name="photo"; filename="lufei.jpg"
MultipartBody.Part photo = MultipartBody.Part.createFormData("photo", file.getName(), photoRequestBody);
RequestBody userName = RequestBody.create(MediaType.parse("text/plain"), "leavesC");
RequestBody password = RequestBody.create(MediaType.parse("text/plain"), "123456");
uploadService.uploadPhoto(photo, userName, password).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
System.out.println("onResponse body: " + response.body().string());
} catch(IOException e) { e.printStackTrace(); }}else {
System.out.println("onResponse code: "+ response.code()); }}@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
System.out.println("onFailure: "+ t.getMessage()); }}); }Copy the code
After the code for uploading files is run, the log information displayed on the server is as follows
Headers header information Key is: content-type, value is: multipart/form-data; boundary=3b8bf455- 620.a- 4250.- 8 -f3d- 8079.Df43d090 Headers Header information key is: Content-Length, value is:224722Headers header information Key is: host, value is: localhost:1995Headers header information Key is: connection, value is: keep-alive Headers header information Key is: accept-encoding, value is: Gzip Headers header Information Key is: user-agent, value is: okhttp/3.10. 0/uploadPhoto fields: {userName: ['leavesC'], password: ['123456' ] }
files : { photo:
[ { fieldName: 'photo',
originalFilename: 'lufei.jpg',
path: '..\\public\\upload\\eKPBTufrJs24ybaoOA2HQ3Aj.jpg',
headers: [Object],
size: 224115}}]Copy the code
The data returned by the server is shown below
{"code":1."msg":"success"."data": {"filePath":".. \\public\\upload\\lfCMVA2VXLNN8XaRmpl-9nE7.jpg"}}
Copy the code
At this point, you can see that the upload folder of the server project has an extra randomly named image
Multi-file upload
Here to achieve multiple files uploaded at the same time
Because the client uses different parameter Settings to implement multiple file uploading, the server needs to adopt different data parsing methods because a new interface is enabled
app.post('/uploadFileDouble'.function (req, res) {
var body = req.body;
for (var key in body) {
console.log("Body key is:", key, " , value is: ", body[key]);
}
/ / request header
var headers = req.headers;
for (var key in headers) {
console.log("Headers header key is:", key, " , value is: ", headers[key]);
}
/ / links
console.log("Url:", req.url);
// Generate the multiparty object and configure the upload destination path
var form = new multiparty.Form({uploadDir: '.. /public/upload/'});
// Fields contains the parameter values passed in
//files represents the file object uploaded to the server
// The file sent from the client will be automatically saved to the specified folder in the background, and the processing result will be notified through the callback function
form.parse(req, function (err, fields, files) {
if (err) {
resultJson.onSystemError(res);
} else {
console.log("fields : ", fields);
console.log("files : ", files);
var filesContent = files['photos'];
var items = [];
for (var index in filesContent) {
varitem = {}; item.filePath = filesContent[index].path; items.push(item); } resultJson.onSuccess(res, items); }}); });Copy the code
The client interface method for uploading multiple files is marked with the @partMap annotation, which holds multiple file forms that need to be uploaded
/** * Author: chenZY * Time: 2018/5/29 18:55 * Description: */
public interface UploadService {
@Multipart
@POST("uploadFileDouble")
Call<ResponseBody> uploadFileDouble(@PartMap Map<String, RequestBody> files);
}
Copy the code
private static void uploadFileDouble(a) {
UploadService uploadService = buildRetrofit().create(UploadService.class);
Map<String, RequestBody> photoMap = new HashMap<>();
File file = new File(".. \\JavaRetrofit\\src\\main\\resources\\images\\lufei.jpg");
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
photoMap.put("photos\"; filename=\"" + file.getName(), photoRequestBody);
file = new File(".. \\JavaRetrofit\\src\\main\\resources\\images\\mingren.jpg");
photoRequestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
photoMap.put("photos\"; filename=\"" + file.getName(), photoRequestBody);
uploadService.uploadFileDouble(photoMap).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
System.out.println("onResponse body: " + response.body().string());
} catch(IOException e) { e.printStackTrace(); }}else {
System.out.println("onResponse code: "+ response.code()); }}@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
System.out.println("onFailure: "+ t.getMessage()); }}); }Copy the code
Run the program, you can see that there are two more different pictures under the upload folder. The log output by the server is as follows
Headers header information Key is: content-type, value is: multipart/form-data; boundary=5c3fcbbb-dd78- 4854.-ad12- 3C4ae3fd1f02 Headers header information Key is: Content-Length, value is:347838Headers header information Key is: host, value is: localhost:1995Headers header information Key is: connection, value is: keep-alive Headers header information Key is: accept-encoding, value is: Gzip Headers header Information Key is: user-agent, value is: okhttp/3.10. 0Url: /uploadFileDouble Fields: {} files: {photos: [{fieldName: 'photos', originalFilename: 'mingren.jpg', path: '..\\public\\upload\\HsvSfjgKtLL3gAqwrxRFk5G-.jpg', headers: [Object], size:123255}, { fieldName: 'photos', originalFilename: 'lufei.jpg', path: '.. \\public\\upload\\bicNIvOD3ZcBe8EgqmSd9SFf.jpg', headers: [Object], size:224115}}]Copy the code
The data received by the client is as follows
{"code":1."msg":"success"."data": [{"filePath":".. \\public\\upload\\HsvSfjgKtLL3gAqwrxRFk5G-.jpg"}, {"filePath":".. \\public\\upload\\bicNIvOD3ZcBe8EgqmSd9SFf.jpg"}}]Copy the code
The download file
Express is highly encapsulated for file downloads, so it may be easier than you think for a server to provide file downloads
This points the file to be downloaded directly to an image in the uplaod folder
app.get('/downloadFile'.function (req, res) {
// File storage path
var filePath = '.. /public/upload/Anoj-VQ-cd_vkw9_O5ErSSG6.jpg';
// Set the file name displayed when the file is downloaded. If this parameter is not set, use the original file name
var fileName = 'leavesC.jpg';
res.download(filePath, fileName);
});
Copy the code
The client creates a DownloadService to declare a method that provides the download function. In order to support large file downloads, the @Streaming annotation is used here to avoid loading the entire file into memory and causing an OOM on Android
/** ** Author: chenZY * Time: 2018/5/30 13:54 * Description: */
public interface DownloadService {
@Streaming
@GET
Call<ResponseBody> downloadFile(@Url String fileUrl);
}
Copy the code
As you can see, the downloaded file is written directly to the desktop using the file read and write method provided by the okIo package
private static void downloadFile(a) {
DownloadService downloadService = buildRetrofit().create(DownloadService.class);
Call<ResponseBody> call = downloadService.downloadFile("downloadFile");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
BufferedSink sink = null;
try {
File file = new File("C:\\Users\\CZY\\Desktop\\Hi.jpg");
sink = Okio.buffer(Okio.sink(file));
sink.writeAll(response.body().source());
System.out.println("onResponse : success");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(sink ! =null) { sink.close(); }}catch(IOException e) { e.printStackTrace(); }}}else {
System.out.println("onResponse code: "+ response.code()); }}@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
System.out.println("onFailure: "+ t.getMessage()); }}); }Copy the code
In addition, if the above code is run on Android system, there is a problem. Because the Callback function is called in the main thread, if the Callback function is performed in the main thread for a long time, it may cause ANR
That’s the end of the example between Retrofit2 and the server. In addition to providing the source code for the client, I’ve also packaged the entire server project together for download
Project home: Retrofit2Samples -> github.com/leavesC/Ret…