AWS S3上传图片并生成预览URL

您所在的位置:网站首页 如何获取图片的url地址 AWS S3上传图片并生成预览URL

AWS S3上传图片并生成预览URL

2023-03-21 17:56| 来源: 网络整理| 查看: 265

前言

为了节约时间,可以直接看文章最后四、最佳实践部分,前面内容是方便已经进入泥潭正面对各种问题的人能搜到本文,找到解决的办法。

官网文档例子是上传后可下载到服务端成一个图片,然后自己组装存放在自己服务器文件夹下图片的访问URL,给用户展现,而不是一个可以供嵌入浏览器页面的图片URL,这样会浪费我们服务器空间并且不能充分利用AWS的带宽。多数情况是:AWS的S3做云存储,把文件上传上去,在数据库中记录对应的URL,HTML页面直接使用这个URL显示图片。

上传图片后,发现直接访问URL,提示没有权限预览,错误信息You are not authorized to perform this operation。这个问题通过我们的AWS后端支持沟通了10个来回,最后发现可以上传成功并且在AWS控制台可以正常预览图片,但我们代码生成的URL不可以浏览的原因是:在中国区需要ICP备案。所以要使用S3和EC2先联系AWS售后支持进行ICP备案。

UnauthorizedAccess You are not authorized to perform this operation FF22D40A1C1F2376 NfNHEkB/e7cC6BkeeESk3Wuh8sBAafXWQYPUWVBFKkaL9yXG+Oc09Kj/j5yGVdVaA8YzTw/kuWw=

当时尝试的解决方案: 一开始以为是权限不够,但是,这个开发环境我们已经把S3的权限放的很大。配置位置如下:

存储桶策略 但是在控制台发现一个可以预览的URL,发现参数有签名信息,这正是解决上面错误提示UnauthorizedAccess的钥匙,于是找预签名URL的方法。由于官方文档没提及此功能是在github的示例代码翻到的此功能,假如他有说明是不是大家可以节约一天的研究时间?差评!

一、官方下载图片的代码

效果是调用此功能,在浏览器提示文件下载成功,可以在下载后的查看图片。

package com.example.s3; // snippet-start:[s3.java2.getobjectdata.import] import software.amazon.awssdk.core.ResponseBytes; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.S3Exception; import software.amazon.awssdk.services.s3.model.GetObjectResponse; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; // snippet-end:[s3.java2.getobjectdata.import] public class GetObjectData { public static void main(String[] args) { if (args.length < 3) { System.out.println("Please specify a bucket name, a key name that represents a PDF file (ie, book.pdf), and a path (ie, C:\\AWS\\AdobePDF.pdf)"); System.exit(1); } String bucketName = args[0]; String keyName = args[1]; String path = args[2]; Region region = Region.US_WEST_2; S3Client s3 = S3Client.builder() .region(region) .build(); getObjectBytes(s3,bucketName,keyName, path); } // snippet-start:[s3.java2.getobjectdata.main] public static void getObjectBytes (S3Client s3, String bucketName, String keyName, String path ) { try { // create a GetObjectRequest instance GetObjectRequest objectRequest = GetObjectRequest .builder() .key(keyName) .bucket(bucketName) .build(); // get the byte[] this AWS S3 object ResponseBytes objectBytes = s3.getObjectAsBytes(objectRequest); byte[] data = objectBytes.asByteArray(); //Write the data to a local file File myFile = new File(path ); OutputStream os = new FileOutputStream(myFile); os.write(data); System.out.println("Successfully obtained bytes from an S3 object"); // Close the file os.close(); } catch (IOException ex) { ex.printStackTrace(); } catch (S3Exception e) { System.err.println(e.awsErrorDetails().errorMessage()); System.exit(1); } // snippet-end:[s3.java2.getobjectdata.main] } } 二、坊间的一个解决方案

此方案代码结构符合我们预期,但是2014年的代码,已经略显陈旧,最新的aws-java-sdk已经不支持这种写法。

public static String uploadToS3(File tempFile, String remoteFileName) throws IOException { PropertiesUtil propertiesUtil = new PropertiesUtil("s3.properties"); //首先创建一个s3的客户端操作对象(需要amazon提供的密钥) AmazonS3 s3 = new AmazonS3Client( new BasicAWSCredentials(propertiesUtil.getKeyValue(Consts.S3_ACCESS_KEY), propertiesUtil.getKeyValue(Consts.S3_SCERET_KEY))); Region usWest2 = Region.getRegion(Regions.US_WEST_2); s3.setRegion(usWest2); //设置bucket,key String bucketName = Consts.S3_BUCKET_NAME; String key = UUID.randomUUID() + ".apk"; try { //验证名称为bucketName的bucket是否存在,不存在则创建 if (!checkBucketExists(s3, bucketName)) { s3.createBucket(bucketName); } //上传文件 s3.putObject(new PutObjectRequest(bucketName, key, tempFile)); S3Object object = s3.getObject(new GetObjectRequest(bucketName, key)); //获取一个request GeneratePresignedUrlRequest urlRequest = new GeneratePresignedUrlRequest( bucketName, key); Date expirationDate = null; try { expirationDate = new SimpleDateFormat("yyyy-MM-dd").parse("2020-12-31"); } catch (Exception e) { e.printStackTrace(); } //设置过期时间 urlRequest.setExpiration(expirationDate); //生成公用的url URL url = s3.generatePresignedUrl(urlRequest); System.out.println("=========URL=================" + url + "============URL============="); if (url == null) { throw new OperateFailureException("can't get s3 file url!"); } return url.toString(); } catch (AmazonServiceException ase) { ase.printStackTrace(); logger.info("====================================AWS S3 UPLOAD ERROR START======================================"); logger.info("Caught an AmazonServiceException, which means your request made it " + "to Amazon S3, but was rejected with an error response for some reason."); logger.info("Caught an AmazonServiceException, which means your request made it " + "to Amazon S3, but was rejected with an error response for some reason."); logger.info("Error Message: " + ase.getMessage()); logger.info("HTTP Status Code: " + ase.getStatusCode()); logger.info("AWS Error Code: " + ase.getErrorCode()); logger.info("Error Type: " + ase.getErrorType()); logger.info("Request ID: " + ase.getRequestId()); logger.info(ase.getMessage(), ase); logger.info("====================================AWS S3 UPLOAD ERROR END======================================"); throw new OperateFailureException("error occurs during upload to s3!"); } catch (AmazonClientException ace) { logger.info("====================================AWS S3 UPLOAD ERROR START======================================"); logger.info("Caught an AmazonClientException, which means the client encountered " + "a serious internal problem while trying to communicate with S3, " + "such as not being able to access the network."); logger.info("Error Message: " + ace.getMessage()); logger.info("====================================AWS S3 UPLOAD ERROR END======================================"); throw new OperateFailureException("error occurs during upload to s3!"); } } /** * 验证s3上是否存在名称为bucketName的Bucket * @param s3 * @param bucketName * @return */ public static boolean checkBucketExists (AmazonS3 s3, String bucketName) { List buckets = s3.listBuckets(); for (Bucket bucket : buckets) { if (Objects.equals(bucket.getName(), bucketName)) { return true; } } return false; } 三、最新版本的上传图片AWS官方示例

不是完全符合我们的要求,没有预览图片的代码逻辑。

package com.example.s3; // snippet-start:[presigned.java2.generatepresignedurl.import] import java.io.IOException; import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.URL; import java.time.Duration; import software.amazon.awssdk.services.s3.model.ObjectCannedACL; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.S3Exception; import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest; import software.amazon.awssdk.services.s3.presigner.S3Presigner; import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; // snippet-end:[presigned.java2.generatepresignedurl.import] public class GeneratePresignedUrlAndUploadObject { public static void main(String[] args) { if (args.length < 2) { System.out.println("Please specify a bucket name and a key name that represents a text file"); System.exit(1); } String bucketName = args[0]; String keyName = args[1]; // Create a S3Presigner by using the default AWS Region and credentials S3Presigner presigner = S3Presigner.create(); signBucket(presigner, bucketName, keyName); } // snippet-start:[presigned.java2.generatepresignedurl.main] public static void signBucket(S3Presigner presigner, String bucketName, String keyName) { try { // Use a PutObjectRequest to set additional values PutObjectRequest objectRequest = PutObjectRequest.builder() .bucket(bucketName) .key(keyName) .contentType("text/plain") .build(); PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder() .signatureDuration(Duration.ofMinutes(10)) .putObjectRequest(objectRequest) .build(); PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest); System.out.println("Pre-signed URL to upload a file to: " + presignedRequest.url()); System.out.println("Which HTTP method needs to be used when uploading a file: " + presignedRequest.httpRequest().method()); // Upload content to the bucket by using this URL URL url = presignedRequest.url(); // Create the connection and use it to upload the new object by using the pre-signed URL HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setRequestProperty("Content-Type","text/plain"); connection.setRequestMethod("PUT"); OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream()); out.write("This text uploaded as an object via presigned URL."); out.close(); connection.getResponseCode(); System.out.println("HTTP response code: " + connection.getResponseCode()); /* * It's recommended that you close the S3Presigner when it is done being used, because some credential * providers (e.g. if your AWS profile is configured to assume an STS role) require system resources * that need to be freed. If you are using one S3Presigner per application (as recommended), this * usually isn't needed */ presigner.close(); } catch (S3Exception e) { e.getStackTrace(); } catch (IOException e) { e.getStackTrace(); } // snippet-end:[presigned.java2.generatepresignedurl.main] } } 四、最佳实践

前提: In China,要使用S3和EC2先联系AWS售后支持进行ICP备案。

依据他官方文档进行桶各种权限的设置,其实默认创建个S3存储桶bucket,不进行任何权限设定即可。 新建IAM的S3用户,生成Access key和seceret Key供后续代码调用。 新建用户 创建访问的密钥,点击创建访问密钥按钮: image.png 引入依赖: software.amazon.awssdk aws-sdk-java 2.14.26 核心方法 以下方法是跑通的一个逻辑,有2个方法,一个是上传图片。另外getPresignedUrl是生成预览图片的带有签名的URL的方法,使用输出的这个URL即可直接预览图片。 package com.erbadagang.aws; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.S3Exception; import software.amazon.awssdk.services.s3.presigner.S3Presigner; import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest; import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; import software.amazon.awssdk.utils.IoUtils; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.time.Duration; public class PresignerAndUploader { public static void main(String[] args) { String bucketName = "dev-npay-s3"; String objectKey = "head_pic.jpg"; Region region = Region.CN_NORTHWEST_1; String accessKeyId = "替换成你的access key"; String secretAccessKey = "替换成你的secret access key"; String presignUrlDurationMinutes = "5000"; long presignUrlDurationMinutesLong = Long.valueOf(presignUrlDurationMinutes); String objectLocalPath = "D:\\backup\\weixin_file\\WeChat Files\\All Users\\116f8f343203cef0107f2ca0f4fc0b03.jpg"; String contentType = "image/jpg"; /* * S3Presigner and credentials. */ AwsBasicCredentials awsBasicCredentials = AwsBasicCredentials.create(accessKeyId, secretAccessKey); S3Presigner s3Presigner = S3Presigner.builder().region(region) .credentialsProvider(StaticCredentialsProvider.create(awsBasicCredentials)).build(); /* * Generate presigned URL. */ URL presignedUrl = generatePresignedUrl(s3Presigner, bucketName, objectKey, region, accessKeyId, secretAccessKey, presignUrlDurationMinutesLong); /* * Upload object to S3. */ uploadObject(presignedUrl, objectLocalPath, contentType); getPresignedUrl(s3Presigner, bucketName, objectKey); } private static URL generatePresignedUrl(S3Presigner s3Presigner, String bucketName, String objectKey, Region region, String accessKeyId, String secretAccessKey, long presignUrlDurationMinutesLong) { URL presignedUrl = null; // PutObjectRequest PutObjectRequest putObjectRequest = PutObjectRequest.builder().bucket(bucketName).key(objectKey).build(); // PutObjectPresignRequest PutObjectPresignRequest putObjectPresignRequest = PutObjectPresignRequest.builder() .signatureDuration(Duration.ofMinutes(presignUrlDurationMinutesLong)).putObjectRequest(putObjectRequest) .build(); // PresignedPutObjectRequest PresignedPutObjectRequest presignedPutObjectRequest = s3Presigner.presignPutObject(putObjectPresignRequest); presignedUrl = presignedPutObjectRequest.url(); System.out.println("Presigned URL: " + presignedUrl); System.out.println("Method needed: " + presignedPutObjectRequest.httpRequest().method()); s3Presigner.close(); return presignedUrl; } public static void getPresignedUrl(S3Presigner presigner, String bucketName, String keyName) { try { // Create a GetObjectRequest to be pre-signed GetObjectRequest getObjectRequest = GetObjectRequest.builder() .bucket(bucketName) .key(keyName) .build(); // Create a GetObjectPresignRequest to specify the signature duration GetObjectPresignRequest getObjectPresignRequest = GetObjectPresignRequest.builder() .signatureDuration(Duration.ofMinutes(1000)) .getObjectRequest(getObjectRequest) .build(); // Generate the presigned request PresignedGetObjectRequest presignedGetObjectRequest = presigner.presignGetObject(getObjectPresignRequest); // Log the presigned URL,这个URL是我们要预览图片的URL System.out.println("Presigned URL: " + presignedGetObjectRequest.url()); // Create a JDK HttpURLConnection for communicating with S3 HttpURLConnection connection = (HttpURLConnection) presignedGetObjectRequest.url().openConnection(); // Specify any headers that the service needs (not needed when isBrowserExecutable is true) presignedGetObjectRequest.httpRequest().headers().forEach((header, values) -> { values.forEach(value -> { connection.addRequestProperty(header, value); }); }); // Send any request payload that the service needs (not needed when isBrowserExecutable is true) if (presignedGetObjectRequest.signedPayload().isPresent()) { connection.setDoOutput(true); try (InputStream signedPayload = presignedGetObjectRequest.signedPayload().get().asInputStream(); OutputStream httpOutputStream = connection.getOutputStream()) { IoUtils.copy(signedPayload, httpOutputStream); } } // Download the result of executing the request try (InputStream content = connection.getInputStream()) { System.out.println("Service returned response: "); IoUtils.copy(content, System.out); } /* * It's recommended that you close the S3Presigner when it is done being used, because some credential * providers (e.g. if your AWS profile is configured to assume an STS role) require system resources * that need to be freed. If you are using one S3Presigner per application (as recommended), this * usually isn't needed */ presigner.close(); } catch (S3Exception e) { e.getStackTrace(); } catch (IOException e) { e.getStackTrace(); } } private static void uploadObject(URL presignedUrl, String objectLocalPath, String contentType) { BufferedOutputStream bufferedOutputStream = null; BufferedInputStream bufferedInputStream = null; try { HttpURLConnection httpURLConnection = (HttpURLConnection) presignedUrl.openConnection(); httpURLConnection.setDoOutput(true); httpURLConnection.setRequestProperty("Content-Type", contentType); httpURLConnection.setRequestMethod("PUT"); bufferedOutputStream = new BufferedOutputStream(httpURLConnection.getOutputStream()); /* * Read from local and write to S3. */ bufferedInputStream = new BufferedInputStream(new FileInputStream(new File(objectLocalPath))); byte[] buffer = new byte[1024]; int length = bufferedInputStream.read(buffer); while (length > 0) { // Write. bufferedOutputStream.write(buffer); // Read next. length = bufferedInputStream.read(buffer); } bufferedOutputStream.flush(); bufferedOutputStream.close(); System.out.println("Response Code: " + httpURLConnection.getResponseCode()); System.out.println("Response Message: " + httpURLConnection.getResponseMessage()); } catch (IOException e) { e.printStackTrace(); } finally { if (bufferedOutputStream != null) { try { bufferedOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (bufferedInputStream != null) { try { bufferedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } }

使用

// Log the presigned URL,这个URL是我们要预览图片的URL System.out.println("Presigned URL: " + presignedGetObjectRequest.url());

打印出来的URL即可正常访问图片。注意:代码里这个URL是有有效期的,为了安全,如果下次再需要使用,需要使用同样的方法生成新的签名url来使用。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3