我正在尝试使用 Retrofit 2.0
对服务器进行HTTP POSTMediaType MEDIA_TYPE_TEXT = MediaType.parse("text/plain");
MediaType MEDIA_TYPE_IMAGE = MediaType.parse("image/*");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
imageBitmap.compress(Bitmap.CompressFormat.JPEG,90,byteArrayOutputStream);
profilePictureByte = byteArrayOutputStream.toByteArray();
Call<APIResults> call = ServiceAPI.updateProfile(
RequestBody.create(MEDIA_TYPE_TEXT, emailString),
RequestBody.create(MEDIA_TYPE_IMAGE, profilePictureByte));
call.enqueue();
服务器返回错误,指出文件无效。
这很奇怪,因为我试图在iOS上使用相同的格式上传相同的文件(使用其他库),但它上传成功。
我想知道使用 Retrofit 2.0 上传图片的正确方法是什么?
我应该在上传之前先将它保存到磁盘吗?
P.S。:我已将改装用于其他不包含图像的Multipart请求,并且已成功完成。问题是当我试图在主体中包含一个字节时。
答案 0 :(得分:151)
我正在强调1.9和2.0中的解决方案,因为它对某些
很有用在1.9
中,我认为更好的解决方案是将文件保存到磁盘并将其用作类型文件,如:
(我不知道你的服务器端实现)有一个类似于这个
的API接口方法@POST("/en/Api/Results/UploadFile")
void UploadFile(@Part("file")TypedFile file,@Part("folder")String folder,Callback<Response> callback);
并像
一样使用它TypedFile file = new TypedFile("multipart/form-data", new File(path));
API接口:
public interface ApiInterface {
@Multipart
@POST("/api/Accounts/editaccount")
Call<User> editUser (@Header("Authorization") String authorization, @Part("file\"; filename=\"pp.png\" ") RequestBody file , @Part("FirstName") RequestBody fname, @Part("Id") RequestBody id);
}
使用它像:
File file = new File(imageUri.getPath());
RequestBody fbody = RequestBody.create(MediaType.parse("image/*"), file);
RequestBody name = RequestBody.create(MediaType.parse("text/plain"), firstNameField.getText().toString());
RequestBody id = RequestBody.create(MediaType.parse("text/plain"), AZUtils.getUserId(this));
Call<User> call = client.editUser(AZUtils.getToken(this), fbody, name, id);
call.enqueue(new Callback<User>() {
@Override
public void onResponse(retrofit.Response<User> response, Retrofit retrofit) {
AZUtils.printObject(response.body());
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
});
答案 1 :(得分:129)
正确上传文件的方式,其名称为 Retrofit 2 ,没有任何 hack :
定义API接口:
@{files}= List Files In Directory Z:\\ *base*.zip
${lastModifiedFile} = Get From List ${files} 0
${time1} = OperatingSystem.Get Modified Time Z://${lastModifiedFile} epoch
:FOR ${file} IN @{files}
\ ${time} Get Modified Time ${file} epoch
\ ${lastModifiedFile} Set Variable If ${time1} < ${time} ${file} ${lastModifiedfile}
\ ${time1} Set Variable If ${time1} < ${time} ${time} ${time1}
像这样上传文件:
@Multipart
@POST("uploadAttachment")
Call<MyResponse> uploadAttachment(@Part MultipartBody.Part filePart);
// You can add other parameters too
这仅演示了文件上传,您还可以使用File file = // initialize file here
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), RequestBody.create(MediaType.parse("image/*"), file));
Call<MyResponse> call = api.uploadAttachment(filePart);
注释在同一方法中添加其他参数。
答案 2 :(得分:18)
我为注册用户使用了Retrofit 2.0,从注册帐户发送了多部分/表单文件图像和文本
在我的RegisterActivity中,使用AsyncTask
//AsyncTask
private class Register extends AsyncTask<String, Void, String> {
@Override
protected void onPreExecute() {..}
@Override
protected String doInBackground(String... params) {
new com.tequilasoft.mesasderegalos.dbo.Register().register(txtNombres, selectedImagePath, txtEmail, txtPassword);
responseMensaje = StaticValues.mensaje ;
mensajeCodigo = StaticValues.mensajeCodigo;
return String.valueOf(StaticValues.code);
}
@Override
protected void onPostExecute(String codeResult) {..}
在我的Register.java类中使用带有同步调用的Retrofit
import android.util.Log;
import com.tequilasoft.mesasderegalos.interfaces.RegisterService;
import com.tequilasoft.mesasderegalos.utils.StaticValues;
import com.tequilasoft.mesasderegalos.utils.Utilities;
import java.io.File;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;
/**Created by sam on 2/09/16.*/
public class Register {
public void register(String nombres, String selectedImagePath, String email, String password){
try {
// create upload service client
RegisterService service = ServiceGenerator.createUser(RegisterService.class);
// add another part within the multipart request
RequestBody requestEmail =
RequestBody.create(
MediaType.parse("multipart/form-data"), email);
// add another part within the multipart request
RequestBody requestPassword =
RequestBody.create(
MediaType.parse("multipart/form-data"), password);
// add another part within the multipart request
RequestBody requestNombres =
RequestBody.create(
MediaType.parse("multipart/form-data"), nombres);
MultipartBody.Part imagenPerfil = null;
if(selectedImagePath!=null){
File file = new File(selectedImagePath);
Log.i("Register","Nombre del archivo "+file.getName());
// create RequestBody instance from file
RequestBody requestFile =
RequestBody.create(MediaType.parse("multipart/form-data"), file);
// MultipartBody.Part is used to send also the actual file name
imagenPerfil = MultipartBody.Part.createFormData("imagenPerfil", file.getName(), requestFile);
}
// finally, execute the request
Call<ResponseBody> call = service.registerUser(imagenPerfil, requestEmail,requestPassword,requestNombres);
Response<ResponseBody> bodyResponse = call.execute();
StaticValues.code = bodyResponse.code();
StaticValues.mensaje = bodyResponse.message();
ResponseBody errorBody = bodyResponse.errorBody();
StaticValues.mensajeCodigo = errorBody==null
?null
:Utilities.mensajeCodigoDeLaRespuestaJSON(bodyResponse.errorBody().byteStream());
Log.i("Register","Code "+StaticValues.code);
Log.i("Register","mensaje "+StaticValues.mensaje);
Log.i("Register","mensajeCodigo "+StaticValues.mensaje);
}
catch (Exception e){
e.printStackTrace();
}
}
}
在RegisterService的接口中
public interface RegisterService {
@Multipart
@POST(StaticValues.REGISTER)
Call<ResponseBody> registerUser(@Part MultipartBody.Part image,
@Part("email") RequestBody email,
@Part("password") RequestBody password,
@Part("nombre") RequestBody nombre
);
}
对于Utilities解析的InputStream响应< / p>
public class Utilities {
public static String mensajeCodigoDeLaRespuestaJSON(InputStream inputStream){
String mensajeCodigo = null;
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(
inputStream, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
inputStream.close();
mensajeCodigo = sb.toString();
} catch (Exception e) {
Log.e("Buffer Error", "Error converting result " + e.toString());
}
return mensajeCodigo;
}
}
答案 3 :(得分:14)
在 Retrofit2.0
中更新图像文件上传的代码public interface ApiInterface {
@Multipart
@POST("user/signup")
Call<UserModelResponse> updateProfilePhotoProcess(@Part("email") RequestBody email, @Part("password") RequestBody password, @Part("profile_pic\"; filename=\"pp.png\" ") RequestBody file);
}
将MediaType.parse("image/*")
更改为MediaType.parse("image/jpeg")
RequestBody reqFile = RequestBody.create(MediaType.parse("image/jpeg"), file);
RequestBody email = RequestBody.create(MediaType.parse("text/plain"), "upload_test4@gmail.com");
RequestBody password = RequestBody.create(MediaType.parse("text/plain"), "123456789");
Call<UserModelResponse> call = apiService.updateProfilePhotoProcess(email,password,reqFile);
call.enqueue(new Callback<UserModelResponse>() {
@Override
public void onResponse(Call<UserModelResponse> call, Response<UserModelResponse> response) {
String TAG = response.body().toString();
UserModelResponse userModelResponse = response.body();
UserModel userModel = userModelResponse.getUserModel();
Log.d("MainActivity","user image = "+userModel.getProfilePic());
}
@Override
public void onFailure(Call<UserModelResponse> call, Throwable t) {
Toast.makeText(MainActivity.this,""+TAG,Toast.LENGTH_LONG).show();
}
});
答案 4 :(得分:10)
添加@insomniac给出的答案。您可以创建Map
来放置RequestBody
的参数,包括图片。
接口代码
public interface ApiInterface {
@Multipart
@POST("/api/Accounts/editaccount")
Call<User> editUser (@Header("Authorization") String authorization, @PartMap Map<String, RequestBody> map);
}
Java类代码
File file = new File(imageUri.getPath());
RequestBody fbody = RequestBody.create(MediaType.parse("image/*"), file);
RequestBody name = RequestBody.create(MediaType.parse("text/plain"), firstNameField.getText().toString());
RequestBody id = RequestBody.create(MediaType.parse("text/plain"), AZUtils.getUserId(this));
Map<String, RequestBody> map = new HashMap<>();
map.put("file\"; filename=\"pp.png\" ", fbody);
map.put("FirstName", name);
map.put("Id", id);
Call<User> call = client.editUser(AZUtils.getToken(this), map);
call.enqueue(new Callback<User>() {
@Override
public void onResponse(retrofit.Response<User> response, Retrofit retrofit)
{
AZUtils.printObject(response.body());
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
});
答案 5 :(得分:2)
使用Retrofit上传文件非常简单您需要将您的api界面构建为
public interface Api {
String BASE_URL = "http://192.168.43.124/ImageUploadApi/";
@Multipart
@POST("yourapipath")
Call<MyResponse> uploadImage(@Part("image\"; filename=\"myfile.jpg\" ") RequestBody file, @Part("desc") RequestBody desc);
}
在上面的代码中图片是关键名称,所以如果你使用的是php,你会写 $ _ FILES [&#39; image&#39;] [&#39; tmp_name&# 39;] 得到这个。 filename =&#34; myfile.jpg&#34; 是随请求一起发送的文件的名称。
现在要上传文件,你需要一个方法,它将为你提供Uri的绝对路径。
private String getRealPathFromURI(Uri contentUri) {
String[] proj = {MediaStore.Images.Media.DATA};
CursorLoader loader = new CursorLoader(this, contentUri, proj, null, null, null);
Cursor cursor = loader.loadInBackground();
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String result = cursor.getString(column_index);
cursor.close();
return result;
}
现在您可以使用以下代码上传文件。
private void uploadFile(Uri fileUri, String desc) {
//creating a file
File file = new File(getRealPathFromURI(fileUri));
//creating request body for file
RequestBody requestFile = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file);
RequestBody descBody = RequestBody.create(MediaType.parse("text/plain"), desc);
//The gson builder
Gson gson = new GsonBuilder()
.setLenient()
.create();
//creating retrofit object
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Api.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
//creating our api
Api api = retrofit.create(Api.class);
//creating a call and calling the upload image method
Call<MyResponse> call = api.uploadImage(requestFile, descBody);
//finally performing the call
call.enqueue(new Callback<MyResponse>() {
@Override
public void onResponse(Call<MyResponse> call, Response<MyResponse> response) {
if (!response.body().error) {
Toast.makeText(getApplicationContext(), "File Uploaded Successfully...", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(), "Some error occurred...", Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Call<MyResponse> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
有关详细说明,请访问 Retrofit Upload File Tutorial 。
答案 6 :(得分:2)
在kotlin中使用 toMediaType , asRequestBody 和 toRequestBody ,这是一个示例:
在这里,我使用multipart发布了几个常规字段以及pdf文件和图像文件
这是使用改型的API声明:
@Multipart
@POST("api/Lesson/AddNewLesson")
fun createLesson(
@Part("userId") userId: RequestBody,
@Part("LessonTitle") lessonTitle: RequestBody,
@Part pdf: MultipartBody.Part,
@Part imageFile: MultipartBody.Part
): Maybe<BaseResponse<String>>
这是实际的调用方式:
api.createLesson(
userId.toRequestBody("text/plain".toMediaType()),
lessonTitle.toRequestBody("text/plain".toMediaType()),
startFromRegister.toString().toRequestBody("text/plain".toMediaType()),
MultipartBody.Part.createFormData(
"jpeg",
imageFile.name,
imageFile.asRequestBody("image/*".toMediaType())
),
MultipartBody.Part.createFormData(
"pdf",
pdfFile.name,
pdfFile.asRequestBody("application/pdf".toMediaType())
)
答案 7 :(得分:0)
Kotlin版本,其中包含RequestBody.create
的更新:
改造界面
@Multipart
@POST("uploadPhoto")
fun uploadFile(@Part file: MultipartBody.Part): Call<FileResponse>
并上传
fun uploadFile(fileUrl: String){
val file = File(fileUrl)
val fileUploadService = RetrofitClientInstance.retrofitInstance.create(FileUploadService::class.java)
val requestBody = file.asRequestBody(file.extension.toMediaTypeOrNull())
val filePart = MultipartBody.Part.createFormData(
"blob",file.name,requestBody
)
val call = fileUploadService.uploadFile(filePart)
call.enqueue(object: Callback<FileResponse>{
override fun onFailure(call: Call<FileResponse>, t: Throwable) {
Log.d(TAG,"Fckd")
}
override fun onResponse(call: Call<FileResponse>, response: Response<FileResponse>) {
Log.d(TAG,"success"+response.toString()+" "+response.body().toString()+" "+response.body()?.status)
}
})
}
感谢@ jimmy0251
答案 8 :(得分:0)
请勿在函数名称中使用多个参数 只需使用一些简单的args约定即可,以提高代码的可读性,为此,您可以像-< / p>
// MultipartBody.Part.createFormData("partName", data)
Call<SomReponse> methodName(@Part MultiPartBody.Part part);
// RequestBody.create(MediaType.get("text/plain"), data)
Call<SomReponse> methodName(@Part(value = "partName") RequestBody part);
/* for single use or you can use by Part name with Request body */
// add multiple list of part as abstraction |ease of readability|
Call<SomReponse> methodName(@Part List<MultiPartBody.Part> parts);
Call<SomReponse> methodName(@PartMap Map<String, RequestBody> parts);
// this way you will save the abstraction of multiple parts.
在使用Retrofit时可能会遇到多个异常,所有异常都记录为代码,有一个演练到retrofit2/RequestFactory.java
。您可以使用两个函数parseParameterAnnotation
和parseMethodAnnotation
进行异常抛出,请仔细阅读,与 googling / stackoverflow 相比,它可以节省大量时间>
答案 9 :(得分:0)
* Return MultipartBody from file path
public static MultipartBody.Part generateFileBody(String imagePath)
{
File file = new File(imagePath);
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
return MultipartBody.Part.createFormData("mediaProfilePic", file.getName(), requestFile);
}