我正在尝试从我的 Android APP上传Image
到Amazon AWS S3 ,我需要使用 AWS Restful API 。
我正在使用Retrofit 2来处理请求。
我的应用程序已成功连接 Amazon S3 并按预期执行请求,但当我尝试从 Bucket 查看Image
时,图片不开放。我将Image
下载到我的电脑上并尝试打开但仍然收到图像已损坏的消息。
让我们看看我的完整代码。
我的Gradle依赖
compile 'com.squareup.retrofit:retrofit:2.0.0-beta1'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta1'
compile 'net.danlew:android.joda:2.8.2'
创建一个文件并启动请求
File file = new File(mCurrentPhotoPath);
RequestBody body = RequestBody.create(MediaType.parse("image/jpeg"), file);
uploadImage(body, "photo_name.jpeg");
改造界面
public interface AwsS3 {
@Multipart
@PUT("/{Key}")
Call<String> upload(@Path("Key") String Key,
@Header("Content-Length") long length,
@Header("Accept") String accept,
@Header("Host") String host,
@Header("Date") String date,
@Header("Content-type") String contentType,
@Header("Authorization") String authorization,
@Part("Body") RequestBody body);
}
使用类来安装凭据
public class AWSOauth {
public static String getOAuthAWS(Context context, String fileName) throws Exception{
String secret = context.getResources().getString(R.string.s3_secret);
String access = context.getResources().getString(R.string.s3_access_key);
String bucket = context.getResources().getString(R.string.s3_bucket);
return gerateOAuthAWS(secret, access, bucket,fileName);
}
private static String gerateOAuthAWS(String secretKey, String accessKey, String bucket, String imageName) throws Exception {
String contentType = "image/jpeg";
DateTimeFormatter fmt = DateTimeFormat.forPattern("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z").withLocale(Locale.US);
String ZONE = "GMT";
DateTime dt = new DateTime();
DateTime dtLondon = dt.withZone(DateTimeZone.forID(ZONE)).plusHours(1);
String formattedDate = dtLondon.toString(fmt);
String resource = "/" + bucket + "/" + imageName;
String stringToSign = "PUT" + "\n\n" + contentType + "\n" + formattedDate + "\n" + resource;
Mac hmac = Mac.getInstance("HmacSHA1");
hmac.init(new SecretKeySpec(secretKey.getBytes("UTF-8"), "HmacSHA1"));
String signature = ( Base64.encodeToString(hmac.doFinal(stringToSign.getBytes("UTF-8")), Base64.DEFAULT)).replaceAll("\n", "");
String oauthAWS = "AWS " + accessKey + ":" + signature;
return oauthAWS;
}
}
最后提出请求的方法
public void uploadImage(RequestBody body, String fileName){
String bucket = getString(R.string.s3_bucket);
Retrofit restAdapter = new Retrofit.Builder()
.baseUrl("http://" + bucket + ".s3.amazonaws.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
AwsS3 service = restAdapter.create(AwsS3.class);
DateTimeFormatter fmt = DateTimeFormat.forPattern("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z").withLocale(Locale.US);
String ZONE = "GMT";
DateTime dt = new DateTime();
DateTime dtLondon = dt.withZone(DateTimeZone.forID(ZONE)).plusHours(1);
String formattedDate = dtLondon.toString(fmt);
try {
String oauth = AWSOauth.getOAuthAWS(getApplicationContext(), fileName);
Call<String> call = service.upload(fileName, body.contentLength(), "/**", bucket + ".s3.amazonaws.com", formattedDate, body.contentType().toString(), oauth, body);
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Response<String> response) {
Log.d("tag", "response : " + response.body());
}
@Override
public void onFailure(Throwable t) {
Log.d("tag", "response : " + t.getMessage());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
我感谢任何帮助,提前谢谢!
答案 0 :(得分:5)
我有同样的问题,当我使用Fiddler检查HTTP请求内容时,我发现改进2.0.0 beta1与1.9.0有所不同。
在我的问题中,不同的HTTP请求内容会阻止服务器获取正确的数据。
为了制作相同的HTTP请求内容,我使用retrofit 2.0.0 deta1执行后续步骤。
在改造服务中,为http请求添加表单数据标头;
@Headers("Content-Type: multipart/form-data;boundary=95416089-b2fd-4eab-9a14-166bb9c5788b")
int retrofit 2.0.0 deta1
,使用@Multipart
的标头会获得如下数据:
内容类型:multipart / mixed
因为聋人的价值是混合的,没有边界标题。
请勿使用@Multipart
上传文件,只需使用@Body
RequestBody
如果您使用@Multipart
请求服务器,则必须通过
@Part(key)
,那么你将遇到一个新问题。可能是改造2.0.0beta1有一个BUG ...,@Multipart
生成一个错误的http请求编译1.9.0。
调用方法时,需要将MultipartRequestBody传递给@Body RequestBody
使用MultipartBuilder
创建MultipartRequestBody
,当您新MultipartBuilder
时,请调用此consturt:
new MultipartBuilder("95416089-b2fd-4eab-9a14-166bb9c5788b")
param是你设置int @headers(boundary=)
builder.addFormDataPart(String name, String filename, RequestBody value)
此方法将有助于形成如下数据的HTTP请求内容:
内容 - 处置:表单数据;名称=&#34; imgFile&#34 ;; 文件名=&#34; IMG_20150911_113029.jpg&#34;内容类型:image / jpg 内容长度:1179469
RequestBody
值是您在代码中生成的值。
我只是暂时解决这个问题。
希望可以帮到你!答案 1 :(得分:3)
您正在发送多部分有效内容,但强制内容类型为image/jpeg
。您的jpg已损坏,因为S3可能已将多部分标头保存到您的jpg文件中,因为您告诉它整个消息是JPG。由于您实际上没有多个部分要发送,因此您可以删除Multipart
注释并使用Body
代替Part
RequestBody
public interface AwsS3 {
@PUT("/{Key}")
Call<String> upload(@Path("Key") String Key,
@Header("Content-Length") long length,
@Header("Accept") String accept,
@Header("Host") String host,
@Header("Date") String date,
@Header("Content-type") String contentType,
@Header("Authorization") String authorization,
@Body RequestBody body);
}
您还应该能够删除明确设置Content-type
和Content-length
标题。
答案 2 :(得分:2)
I have used Retrofit 2 resolve and I use Body instead of Part for your RequestBody in interface
@PUT("")Call<String> nameAPI(@Url String url ,@Body RequestBody body);
和java代码
//prepare image file
File file = new File(pathImg);
RequestBody requestBody = RequestBody.create(MediaType.parse("image/jpeg"), file);
Call<String> call = SingletonApiServiceS3.getInstance().getService().nameAPI(
path,
requestBody
);
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, final Response<String> response) {
if (response.isSuccessful()) {
} else {
}
}
@Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(getContext(),"onFailure : "+t.getMessage().toString(),Toast.LENGTH_SHORT).show();
}
});
答案 3 :(得分:1)
我没有使用Retrofit 2,只是改造1,所以YMMV,但我相信你要做的事情的典型方法是在尝试使用RequestBody时使用TypedFile。
我猜测Retrofit在内部使用RequestBody。
您可以创建TypedFile,如:
TypedFile typedFile = new TypedFile("multipart/form-data", new File("path/to/your/file"));
,您的界面将是:
@Multipart
@PUT("/{Key}")
Call<String> upload(@Path("Key") String Key,
@Header("Content-Length") long length,
@Header("Accept") String accept,
@Header("Host") String host,
@Header("Date") String date,
@Header("Content-type") String contentType,
@Header("Authorization") String authorization,
@Part("Body") TypedFile body);
}
有一个很好的例子 https://futurestud.io/blog/retrofit-how-to-upload-files/
答案 4 :(得分:1)
RequestBody avatarBody = RequestBody.create(MediaType.parse("image"),file);
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), avatarBody);
@Multipart
@POST(url)
Call<ResponseBody> uploadImageAmazon(
@Part MultipartBody.Part filePart);
我有相同的经验,并通过https://github.com/square/retrofit/issues/2424此解决方案
解决了这个问题答案 5 :(得分:0)
您可以使用改装2上传图像/文件
@Multipart
@POST("/api/attachment")
Call<JsonPrimitive> addAttachment(@Part MultipartBody.Part imageFile);
现在打电话:
RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
MultipartBody.Part imageFileBody = MultipartBody.Part.createFormData("file", file.getName(), requestBody);
注意:请确保您使用的是retrofit2,因为我无法使用retrofit1库上传图片。