Ответ, который я дам вам, будет использовать Gson streaming (streaming for speed) и okhttp3 . Пожалуйста, помните, что этот код в том виде, в котором он отображается в данный момент, не был протестирован. Это чтобы показать вам, что делать. Я взял его из одного из моих текущих приложений (идея реализована и работает). Это может выглядеть как излишество. Если у вас есть еще один вопрос из-за точки размытия, оставьте комментарий ниже.
1- Настройка GSON с дооснащением:
package whatever.package.you.want;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
public class DataService {
private static Retrofit retrofit = null;
private static final int CONNECTION_TIMEOUT = 45;//s
private static final int READ_TIMEOUT = 45;//s
private static final int WRITE_TIMEOUT = 45;//s
private static final String MEDIA_TYPE = "application/json";//"multipart/form-data"; //"text/plain";
private static final DATA_SERVICE_BASE_URL = "https://stackoverflow.com"; // your desired URL
//I suppose you have your custom declarations here
public static Retrofit getClient(String yourURL) {
Gson gson = new GsonBuilder()
.setLenient()
.setPrettyPrinting()
.create();
//https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor
/*HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);*/
final OkHttpClient client = new OkHttpClient.Builder()
/*.addInterceptor(logging)*/
.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.build();
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(yourURL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson)) //https://github.com/square/retrofit/tree/master/retrofit-converters/gson
.addConverterFactory(ScalarsConverterFactory.create()) //https://github.com/square/retrofit/tree/master/retrofit-converters/scalars
.build();
}
return retrofit;
}
public static DataService getUserDataService() {
return getClient(DATA_SERVICE_BASE_URL).create(UserDataServiceInterface.class);
}
}
2- Ваша модель MediaUploadResponse.class
:
package whatever.package.you.want;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
@JsonAdapter(MediaUploadResponseAdapter.class)
public class MediaUploadResponse {
@SerializedName("fileName")
private String fileName = "";
@SerializedName("fileId")
private String fileID = "";
@SerializedName("fileSizeInBytes")
private long fileSizeInBytes = "";
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileId() {
return fileId;
}
public void setFileId(String fileId) {
this.fileId = fileId;
}
public String getFileSizeInBytes() {
return fileSizeInBytes;
}
public void setFileSizeInBytes(long fileSizeInBytes) {
this.fileSizeInBytes = fileSizeInBytes;
}
}
3- Адаптер модели MediaUploadResponseAdapter.class
, для сериализации и десериализации:
package whatever.package.you.want;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import whatever.MediaUploadResponse;
import whatever.JsonAdapterUtils;
import java.io.IOException;
public class MediaUploadResponseAdapter extends BaseJsonAdapter<MediaUploadResponse> {
@Override
public MediaUploadResponse read(JsonReader reader) throws IOException {
MediaUploadResponse element = new MediaUploadResponse();
String fieldName = null;
if(reader.peek() == JsonToken.NULL){
reader.nextNull();
return null;
}
reader.beginObject();
while (reader.hasNext()) {
JsonToken token = reader.peek();
if(token.equals(JsonToken.NAME))
fieldName = reader.nextName();
if (fieldName.equals("fileName") && token != JsonToken.NULL)
element.setFileName(JsonAdapterUtils.stringFromJsonReader(reader));
else if (fieldName.equals("fileId") && token != JsonToken.NULL)
element.setFileID(JsonAdapterUtils.stringFromJsonReader(reader));
else if (fieldName.equals("fileSizeInBytes") && token != JsonToken.NULL)
element.SetFileSizeInBytes(JsonAdapterUtils.longFromJsonReader(reader));
else
reader.skipValue();
}
reader.endObject();
return element;
}
@Override
public void write(JsonWriter writer, MediaUploadResponse element) throws IOException {
if(element == null){
writer.nullValue();
return;
}
writer.beginObject();
writer.name("fileName").value(element.getFileName());
writer.name("fileId").value(element.getFileId());
writer.name("fileSizeInBytes").value(element.getFileSizeInBytes());
writer.endObject();
}
}
4- Используйте этот вызов (второй, который вы отправили) ( РЕДАКТИРОВАТЬ: Вот ответ на главную проблему ):
@Headers({
"Accept: application/json"
})
@POST("sync/mediaUpload")
@Multipart
Call<MediaUploadResponse> uploadMediaFile(@Header("Authorization") String token,
@Part("userId") RequestBody userId,
@Part MultipartBody.Part file,
@Part("fileId") RequestBody photoId,
@Part("hash") RequestBody hash);
5- Некоторые бонусы, чтобы вы не пропустили некоторые зависимости:
a- Класс BaseJsonAdapter.class
(поможет разобрать списки):
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
/**
* Created by mamboa on 3/6/2018.
*/
public class BaseJsonAdapter<T> extends TypeAdapter<T>{
public ArrayList<T> readArray(JsonReader reader) throws IOException {
if(reader.peek() == JsonToken.NULL){
reader.nextNull();
return null;
}
ArrayList<T> elements = new ArrayList<T>();
reader.beginArray();
while (reader.hasNext()) {
T value = read(reader);
if(value != null)
elements.add(value);
else {
break;
}
}
reader.endArray();
return elements;
}
public void writeArray(JsonWriter writer, List<T> messages) throws IOException {
writer.beginArray();
for (T message : messages) {
write(writer, message);
}
writer.endArray();
}
public T read(JsonReader reader) throws IOException {
return null;
}
public void write(JsonWriter writer, T t) throws IOException {
}
}
b- И, наконец, JsonAdapterUtils
:
package whatever.Utils;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import java.io.IOException;
/**
* Created by mamboa on 3/7/2018.
*/
public class JsonAdapterUtils {
public static final int INTEGER_DEFAULT = -1;
public static final String STRING_DEFAULT = "";
public static final boolean BOOLEAN_DEFAULT = false;
public static int intFromJsonReader(JsonReader reader) throws IOException{
try {
if(reader.peek() == JsonToken.BOOLEAN)
return fromBooleanToInt(reader.nextBoolean());
String resultValue = reader.nextString();
if("".equals(resultValue))
return INTEGER_DEFAULT;
return Integer.parseInt(resultValue);
}
catch (IOException ex){
return returnDefaultAfterException(reader);
}
catch (IllegalStateException ex){
return returnDefaultAfterException(reader);
}
catch (NumberFormatException ex){
return returnDefaultAfterException(reader);
}
}
public static long longFromJsonReader(JsonReader reader) throws IOException{
try {
if(reader.peek() == JsonToken.BOOLEAN)
return fromBooleanToInt(reader.nextBoolean());
String resultValue = reader.nextString();
if("".equals(resultValue))
return INTEGER_DEFAULT;
return Long.parseLong(resultValue);
}
catch (IOException ex){
return returnDefaultAfterException(reader);
}
catch (IllegalStateException ex){
return returnDefaultAfterException(reader);
}
catch (NumberFormatException ex){
return returnDefaultAfterException(reader);
}
}
public static float floatFromJsonReader(JsonReader reader) throws IOException{
try {
if(reader.peek() == JsonToken.BOOLEAN)
return fromBooleanToInt(reader.nextBoolean());
String resultValue = reader.nextString();
if("".equals(resultValue))
return INTEGER_DEFAULT;
return Float.parseFloat(resultValue);
}
catch (IOException ex){
return returnDefaultAfterException(reader);
}
catch (IllegalStateException ex){
return returnDefaultAfterException(reader);
}
catch (NumberFormatException ex){
return returnDefaultAfterException(reader);
}
}
public static double doubleFromJsonReader(JsonReader reader) throws IOException{
try {
if(reader.peek() == JsonToken.BOOLEAN)
return fromBooleanToInt(reader.nextBoolean());
String resultValue = reader.nextString();
if("".equals(resultValue))
return INTEGER_DEFAULT;
return Double.parseDouble(resultValue);
}
catch (IOException ex){
return returnDefaultAfterException(reader);
}
catch (IllegalStateException ex){
return returnDefaultAfterException(reader);
}
catch (NumberFormatException ex){
return returnDefaultAfterException(reader);
}
}
public static String stringFromJsonReader(JsonReader reader) throws IOException{
String resultValue = "";
try {
if(reader.peek() == JsonToken.BOOLEAN)
return boolFromJsonReader(reader)? "true" : "false";
resultValue = reader.nextString();
return !resultValue.equals("") ? resultValue : STRING_DEFAULT;
}
catch (IOException ex){
reader.skipValue();
return STRING_DEFAULT;
}
catch (IllegalStateException ex){
reader.skipValue();
return STRING_DEFAULT;
}
}
public static boolean boolFromJsonReader(JsonReader reader)throws IOException{
try {
if(reader.peek() == JsonToken.BOOLEAN)
return reader.peek() == JsonToken.BOOLEAN ? reader.nextBoolean() : BOOLEAN_DEFAULT;
}
catch (IOException ex){
reader.skipValue();
}
return BOOLEAN_DEFAULT;
}
private static int returnDefaultAfterException(JsonReader reader) throws IOException {
if(reader != null) reader.skipValue();
return INTEGER_DEFAULT;
}
private static int fromBooleanToInt(boolean value){
return value ? 1 : 0;
}
public static String serializeObject(Object object){
if(object != null) {
Gson gson = new Gson();
return gson.toJson(object);
}
return "";
}
}
РЕДАКТИРОВАТЬ: Проблема:
Проблема в том, что в параметрах http-запроса сервер должен знать, что вызывающая сторона хочет получить ответ в формате JSON.
Таким образом, решение с Retrofit 2 при использовании Multipart
должно добавить следующее (поверх запроса):
@Headers({
"Accept: application/json"
})