我正在使用Retrofit 2创建一个多部分表单数据请求,该请求可以正常运行,并且服务器响应200。我在解析响应时遇到问题。这是我的代码:
@POST("sync/mediaUpload")
@Multipart
Call<ResponseBody> uploadMediaFile(@Header("Authorization") String token,
@Part("userId") RequestBody userId,
@Part MultipartBody.Part file,
@Part("fileId") RequestBody photoId,
@Part("hash") RequestBody hash);
public Response<ResponseBody> uploadMediaFile(String token, String userId, File file, String fileName, String fileId, String hash) {
MediaService service = retrofit.create(MediaService.class);
MultipartBody.Part fileBody = prepareFilePart("file", file);
RequestBody userIdBody = RequestBody.create(MediaType.parse("text/plain"), userId);
RequestBody fileNameBody = RequestBody.create(MediaType.parse("text/plain"), fileName);
RequestBody fileIdBody = RequestBody.create(MediaType.parse("text/plain"), fileId);
RequestBody hashBody = RequestBody.create(MediaType.parse("text/plain"), hash);
Call<ResponseBody> call = service.uploadMediaFile(token, userIdBody, txIdBody, transIdBody, stepCodeBody,
fileBody, fileNameBody, fileIdBody, hashBody);
try {
return call.execute();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
@NonNull
private MultipartBody.Part prepareFilePart(String partName, File file) {
RequestBody requestFile = RequestBody.create(MediaType.parse("image/*"), file);
return MultipartBody.Part.createFormData(partName, file.getName(), requestFile);
}
正确上传文件后,服务器将返回Json对象。示例:
{
"fileName": "IMG_20190108_183751.jpg",
"fileId": "0",
"fileSizeInBytes": 216067
}
但是,在call.execute()中,改造会返回:
--MultipartDataMediaFormatterBoundary1q2w3e
Content-Disposition: form-data; name="FileName"
IMG_20190108_183751.jpg
--MultipartDataMediaFormatterBoundary1q2w3e
Content-Disposition: form-data; name="FileId"
0
--MultipartDataMediaFormatterBoundary1q2w3e
Content-Disposition: form-data; name="FileSizeInBytes"
216067
如何解析该回复? 我试图通过使用对象而不是ResponseBody来更改改装服务的签名:
@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);
还有我的对象
public class MediaUploadResponse {
public final String fileName;
public final String fileId;
public final long fileSizeInBytes;
public MediaUploadResponse(String fileName, String fileId, long
fileSizeInBytes) {
this.fileName = fileName;
this.fileId = fileId;
this.fileSizeInBytes = fileSizeInBytes;
}
}
但是翻新会引发MalformedJsonException
有人知道如何解决吗?
谢谢。
答案 0 :(得分:0)
我将为您提供的答案将使用Gson streaming(以流的形式传输)和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格式的响应。
因此,使用Multipart
时使用Retrofit 2的解决方案是在请求的顶部添加以下内容:
@Headers({
"Accept: application/json"
})