Javaを䜿甚したGoogle Cloud Storageクラりド内の画像ずその他のファむル

Google App Engine / Google Cloud EndpointsプラットフォヌムでのJava Web開発に関する䞀連の蚘事を続けお、Google Cloud Storageファむルのクラりドストレヌゞのサヌビスを芋おいきたす。

䞀般に、スキヌムは次のずおりです。バック゚ンドのサヌバヌは、ストレヌゞの特定のコンテナバケットにファむルを転送するための䞀時リンクアドレスを生成し、フロント゚ンドのファむルを転送するフォヌムに挿入したす。 ナヌザヌは、指定されたアドレスに1぀以䞊のファむルをリク゚スト本文に指定しおPOST HTTPリク゚ストを送信し、ファむルを受信しお​​リポゞトリに配眮したす。配眮されたファむルに関するデヌタずずもにHTTPリク゚ストをサヌブレットが受信したす。サヌブレットは、配眮されたファむルに関する情報を凊理した埌、HTTPレスポンスをナヌザヌに返したす JSONたたはtext / html、たたは私たちが望むもの。

ファむルはリポゞトリに保存され、サヌブレットにはファむルぞのアクセスを蚱可するキヌがありたす。特に、別のサヌブレットを䜿甚しおナヌザヌにファむルを発行するか、「静的」リンクhttps//を䜜成できたす。
リポゞトリぞのアクセスは、りェブむンタヌフェヌスから、およびgsutilナヌティリティを䜿甚しおコマンドラむンからも利甚できたす。

䟋ずしお、Google Cloud StorageをGAEのアプリケヌションず統合したすhello-habrahabr-api.appspot.com + hello-habrahabr-webapp.appspot.comは前の䟋で䜿甚したした。

Google Cloud StorageをGoogle App Engine / Google Cloud Endpointsのプロゞェクトに接続したす


たず、App Engine Developerコン゜ヌルにアクセスしたす appengine.google.com/dashboard ?& app_id=hello-habrahabr-api https://appengine.google.com/dashboard?&app_id={ project ID}

[アプリケヌションの蚭定]> [クラりド統合]に移動し、ペヌゞの䞋郚にある[䜜成]をクリックしたす。

画像

「クラりド統合タスクが開始されたした」ずいうメッセヌゞが衚瀺されたす。

Google Developer Consoleには「叀い」ず「新しい」の2぀のバヌゞョンがあり、機胜は埐々に「叀い」から「新しい」に移行しおいたす。 叀い開発者コン゜ヌルからCloud Integrationを含めおいたすこの機胜はすぐに新しいコン゜ヌルにも登堎するこずを期埅すべきです。

「䜜成」ボタンの代わりに、クラりド統合セクションの䞋郚にペヌゞをリロヌドするず、「プロゞェクトが正垞に䜜成されたした。 詳现に぀いおは、「基本」セクションを参照しおください。「基本セクションの少し䞊に、接続されたGoogle Cloud Storageバケットぞのリンクが衚瀺されたす。デフォルトでは、GAEプロゞェクトず同じ名前が付けられたす私の堎合はhello-habrahabr-api.appspot.com。

画像

リンクをクリックするず、アドレスconsole.developers.google.com/storage/browser {name Bucket} / Dに移動したす私の堎合 console.developers.google.com/storage/browser/hello-habrahabr-api.appspot.com 圓然のこずながら承認が必芁です、ストレヌゞブラりザにアクセスしたす

画像

ここでは、新しいフォルダヌの䜜成、ファむルのアップロヌドず削陀、ファむルのアクセス蚱可の管理を行うこずができたす。たずえば、ファむルを公開し、Web甚のファむルぞのパヌマリンクを受け取るこずができたすたずえば、Web甚に画像やその他の静的ファむルを䜿甚する堎合サむト、怜玢およびフィルタヌ。

Cloud Storageは、Google App Engineのすべおのアプリケヌションに無料のバケットを提䟛したすが、この堎合、ストレヌゞブラりザヌのりェブむンタヌフェヌスはバケットのコンテンツを衚瀺する機胜のみを提䟛したす。 ストレヌゞブラりザヌのすべおの機胜をアクティブにし、远加のバケットを䜜成するには、請求を有効にしおクレゞットカヌド情報を入力する必芁がありたす「無料トラむアルに眲名」をクリックしおクレゞットカヌド情報を入力するず、トラむアル期間である60日間、たたは300ドル以内で無料トラむアルが埗られたす 

ファむルをダりンロヌドするための䞀時リンクを䜜成する


必芁なむンポヌト

import com.google.appengine.api.blobstore.BlobstoreService; import com.google.appengine.api.blobstore.BlobstoreServiceFactory; import com.google.appengine.api.blobstore.UploadOptions; 

リンクを䜜成するコマンド

 String uploadUrl = BlobstoreServiceFactory.getBlobstoreService().createUploadUrl( "/upload", // path to upload handler (servlet) UploadOptions.Builder.withGoogleStorageBucketName("hello-habrahabr-api.appspot.com") // bucket name ) 

たずえば、Cloud EndpointsでAPIを䜜成するず、ファむルをダりンロヌドするためのリンクを返すAPIは次のようになりたす。

 package com.appspot.hello_habrahabr_api; import com.google.api.server.spi.config.Api; import com.google.api.server.spi.config.ApiMethod; import com.google.appengine.api.blobstore.BlobstoreServiceFactory; import com.google.appengine.api.blobstore.UploadOptions; import java.io.Serializable; @Api(name = "uploadAPI", version = "ver.1.0", scopes = {Constants.EMAIL_SCOPE}, clientIds = { Constants.WEB_CLIENT_ID, Constants.API_EXPLORER_CLIENT_ID }, description = "uploads API") public class UploadAPI { // add this class to <init-param> of <servlet-name>SystemServiceServlet</servlet-name> in web.xml /* API methods can return JavaBean Objects only, so we use this as a wrapper for String */ class StringWrapperObject implements Serializable { private String string; public StringWrapperObject() { } public StringWrapperObject(String string) { this.string = string; } public String getString() { return string; } public void setString(String string) { this.string = string; } } // end of StringWrapperObject class @ApiMethod( name = "getCsUploadURL", path = "getCsUploadURL", httpMethod = ApiMethod.HttpMethod.POST ) @SuppressWarnings("unused") public StringWrapperObject getCsUploadURL() { String uploadURL = BlobstoreServiceFactory.getBlobstoreService().createUploadUrl( "/cs-upload", // upload handler servlet address UploadOptions.Builder.withGoogleStorageBucketName( "hello-habrahabr-api.appspot.com" // Cloud Storage bucket name ) ); return new StringWrapperObject(uploadURL); } } 

フロント゚ンドフォヌム
 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script> <title>File Upload Form</title> </head> <body> <hr> One File: <hr> <form action="" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <input type="submit" value="Upload"> </form> <hr> Multiple Files: <hr> <form action="" method="post" enctype="multipart/form-data"> <input type="file" name="files[]" multiple> <input type="submit" value="Upload"> </form> <!-- JavaScript --> <script> 'use strict'; $(document).ready(function () { var url = "https://hello-habrahabr-api.appspot.com/_ah/api/uploadAPI/ver.1.0/getCsUploadURL"; $.ajax(url, { method: "POST", // (default: 'GET') async: false, // default: true processData: true, // (default: true) (By default, data passed in to the 'data' option as an object) success: function (data) { console.log("responce received:"); console.log(data); $("form").attr("action", data.string); } }) }) </script> </body> </html> 

JSPず同じ圢匏
 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="com.google.appengine.api.blobstore.BlobstoreService" %> <%@ page import="com.google.appengine.api.blobstore.BlobstoreServiceFactory" %> <%@ page import="com.google.appengine.api.blobstore.UploadOptions" %> <% BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService(); %> <html> <head> <title>File Upload Form</title> </head> <body> <hr> One File: <hr> <form action="<%= blobstoreService.createUploadUrl("/cs-upload", UploadOptions.Builder.withGoogleStorageBucketName("hello-habrahabr-api.appspot.com")) %>" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <input type="submit" value="Upload"> </form> <hr> Multiple Files: <hr> <form action="<%= blobstoreService.createUploadUrl("/cs-upload", UploadOptions.Builder.withGoogleStorageBucketName("hello-habrahabr-api.appspot.com")) %>" method="post" enctype="multipart/form-data"> <input type="file" name="files[]" multiple> <input type="submit" value="Upload"> </form> </body> </html> 

衚瀺されるリンクは次のようになりたす。

https://hello-habrahabr-api.appspot.com/_ah/upload/AMmfu6YJ0ci-sKP5k98sKaJEUjYwBFbkVfQ7iylXTJV52_gy5HIECKNG52IPUCJ9PB3wpL2wxgX82GkGkzetHt-6fuu4yzAzFFhD8HGOcD7eJ48KJLnKnb2EqbuoFEdyuc8r_FTR7779IIaf42rf_jhkl7Hju3GxWDmxh2WtmcPR2AbB9OWlQhYxBIWtZgBW9OsHO50pI21/ALBNUaYAAAAAVp2DRSZYST46t2kPmrGrrBoY3AFjyOiD/


ただし、HTTP応答は、アドレス/cs-uploadあるサヌブレットによっお䜜成されたす

HTTP応答を生成するサヌブレットアップロヌドハンドラヌ


このサヌブレットは次のようになりたす。

 package com.appspot.hello_habrahabr_api; import com.google.appengine.api.blobstore.BlobKey; import com.google.appengine.api.blobstore.BlobstoreServiceFactory; import com.google.appengine.api.blobstore.FileInfo; import com.google.appengine.api.images.ImagesServiceFactory; import com.google.appengine.api.images.ServingUrlOptions; import com.google.appengine.repackaged.com.google.gson.Gson; import com.google.appengine.repackaged.com.google.gson.GsonBuilder; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.logging.Logger; public class CSUploadHandlerServlet extends HttpServlet { private final static Logger LOG = Logger.getLogger(CSUploadHandlerServlet.class.getName()); private final static String HOST = "https://hello-habrahabr-api.appspot.com"; /* Object to be returned as JSON in HTTP-response (and can be stored in data base) */ class UploadedFileData { FileInfo fileInfo; String BlobKey; String fileServeServletLink; String servingUrlFromgsObjectName; String servingUrlFromGsBlobKey; } // end of uploadedFileData @Override public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // Returns the FileInfo for any files that were uploaded, keyed by the upload form "name" field. // This method should only be called from within a request served by the destination of a createUploadUrl call. // https://cloud.google.com/appengine/docs/java/javadoc/com/google/appengine/api/blobstore/BlobstoreService#getFileInfos-HttpServletRequest- java.util.Map<java.lang.String, java.util.List<FileInfo>> fileInfoListsMap = BlobstoreServiceFactory.getBlobstoreService().getFileInfos(req); LOG.warning("[LOGGER]: " + new Gson().toJson(fileInfoListsMap)); ArrayList<UploadedFileData> uploadedFilesDataList = new ArrayList<>(); for (java.util.List<FileInfo> fileInfoList : fileInfoListsMap.values()) { for (FileInfo fileInfo : fileInfoList) { UploadedFileData uploadedFileData = new UploadedFileData(); uploadedFileData.fileInfo = fileInfo; LOG.warning("uploadedFileData created:" + new Gson().toJson(uploadedFileData)); BlobKey blobKey = BlobstoreServiceFactory.getBlobstoreService().createGsBlobKey(fileInfo.getGsObjectName()); uploadedFileData.BlobKey = blobKey.getKeyString(); uploadedFileData.fileServeServletLink = HOST + "/serve?blob-key=" + blobKey.getKeyString(); // Use Images Java API to create serving URL // works only for images (PNG, JPEG, GIF, TIFF, BMP, ICO, WEBP) for (com.google.appengine.api.images.Image.Format type : com.google.appengine.api.images.Image.Format.values()) { LOG.warning("com.google.appengine.api.images.Image.Format type: " + type.toString()); LOG.warning("fileInfo.getContentType(): " + fileInfo.getContentType()); if (fileInfo.getContentType().toLowerCase().contains(type.toString().toLowerCase())) { uploadedFileData.servingUrlFromgsObjectName = ImagesServiceFactory.getImagesService().getServingUrl(ServingUrlOptions.Builder.withGoogleStorageFileName(fileInfo.getGsObjectName())); // should be the same as servingUrlFromGsBlobKey uploadedFileData.servingUrlFromGsBlobKey = ImagesServiceFactory.getImagesService().getServingUrl(ServingUrlOptions.Builder.withBlobKey(blobKey)); // should be the same as servingUrlFromgsObjectName } } uploadedFilesDataList.add(uploadedFileData); } } res.setContentType("application/json"); res.setCharacterEncoding("UTF-8"); PrintWriter pw = res.getWriter(); //get the stream to write the data Gson gson = new GsonBuilder().disableHtmlEscaping().create(); pw.println( gson.toJson(uploadedFilesDataList) ); LOG.warning("uploadedFilesDataMap" + new Gson().toJson(uploadedFilesDataList)); pw.close(); //closing the stream } // doPost End } 

HTTPレスポンスで次のJSONを生成したす。
ゞョン゜ン
 [{ "fileInfo": { "contentType": "image/svg+xml", "creation": "Jan 18, 2016 7:16:18 PM", "filename": "Sun_symbol.svg", "size": 188, "md5Hash": "YWZmM2UzMzk2ZDk2NTc0ZWM3NDI0YjYyMDMxZGIxYTM=", "gsObjectName": "/gs/hello-habrahabr-api.appspot.com/L2FwcGhvc3RpbmdfcHJvZC9ibG9icy9BRW5CMlVxM3RWVU9nMWVxdmxfQ1dlSk5HaXIwNHpwamE3Y2FhVzlwRjF3dk4zQnFlU3JBMVkxaERGT2NRbXpLZ0pBOW0yTjZiaXhsZjFGWElmdFRNX1p2WXF4WTJnTlF1Zy42LWZnTzcybk1xNG03X1pw" }, "BlobKey": "AMIfv968eMcYEHQml68MAl4NVtOQGKjXUWadeyP7njbaVhHXq_1xDAnRQgHeHrOv4RPLm-KdmqEHP5nb1zNuCFFszRxOVUV4Z97B9slNi7SSGWZ1qKbYcbJi2nl5Z7JX9g1xN4RclYpmLPLfh5k2jAULi6p9g84JSyh5uP3RDkNnPuXkBjxSuBTOWCVxOmpRS-xsB1YedYAgF6cRYLq0hVpm_bOY3Cbl3Ai0W-_req9jxcuPWkoguhHiZ2SSBRF9NlvgG_hCf3vouYtYS2O9DBbioeOL_p1Ck8gfvhQiiK6XpXM4S7vAYqYZCQKJ_9T4tswy075-e6NlsdtXGj9zhSxCy_GfSSBrnvbwcQUDA7lN_IYIfm0QWs-XgzBl9izizUeE46jOI-1O", "fileServeServletLink": "https://hello-habrahabr-api.appspot.com/serve?blob-key=AMIfv968eMcYEHQml68MAl4NVtOQGKjXUWadeyP7njbaVhHXq_1xDAnRQgHeHrOv4RPLm-KdmqEHP5nb1zNuCFFszRxOVUV4Z97B9slNi7SSGWZ1qKbYcbJi2nl5Z7JX9g1xN4RclYpmLPLfh5k2jAULi6p9g84JSyh5uP3RDkNnPuXkBjxSuBTOWCVxOmpRS-xsB1YedYAgF6cRYLq0hVpm_bOY3Cbl3Ai0W-_req9jxcuPWkoguhHiZ2SSBRF9NlvgG_hCf3vouYtYS2O9DBbioeOL_p1Ck8gfvhQiiK6XpXM4S7vAYqYZCQKJ_9T4tswy075-e6NlsdtXGj9zhSxCy_GfSSBrnvbwcQUDA7lN_IYIfm0QWs-XgzBl9izizUeE46jOI-1O" }, { "fileInfo": { "contentType": "image/jpeg", "creation": "Jan 18, 2016 7:16:18 PM", "filename": "world_map_04.jpg", "size": 44680, "md5Hash": "MzQyMzliZGQ4NmYyNmZiNzc3ZjAyMzBhNmM4NDVmNWE=", "gsObjectName": "/gs/hello-habrahabr-api.appspot.com/L2FwcGhvc3RpbmdfcHJvZC9ibG9icy9BRW5CMlVxM3RWVU9nMWVxdmxfQ1dlSk5HaXIwNHpwamE3Y2FhVzlwRjF3dk4zQnFlU3JBMVkxaERGT2NRbXpLZ0pBOW0yTjZiaXhsZjFGWElmdFRNX1p2WXF4WTJnTlF1Zy5Ld1pSRTQ2M3J3ZWYxa3Bm" }, "BlobKey": "AMIfv95nBw0rYnC39nCATxvyecFw0JEe64eTm-OhpsSsrR3Idv_rPbO2c6xTDx3q1xkulXfUyapqtEXdeQQur7FcppXa9rRcnlF7QnU8jur7a7AP3T5Ze_-bdD_F6F5mGP9Tteo7p7cN4UccqoYhnAyabAIsJBq3pZIwX2NlHhqcK_aelnu1tl3aszZU4cVmhLiZGE8hFvgDQyt-2oB4DurXUKTwGC56cZykCdYONO0EDETgkImiytbtk1iV_muyYZzfd7on3OS0LSmY8ls7QIcm1IMgl5jDPJANlsk_iWtnRJfEiYAC9pZ7DfhSPxTeYzko0b1TXrKuGjpG8cYMcxiA0Cmeya8y-7SCQuWQLlKCX8WFpIVOr26UguDaq8SFYplALbxgQUiB", "fileServeServletLink": "https://hello-habrahabr-api.appspot.com/serve?blob-key=AMIfv95nBw0rYnC39nCATxvyecFw0JEe64eTm-OhpsSsrR3Idv_rPbO2c6xTDx3q1xkulXfUyapqtEXdeQQur7FcppXa9rRcnlF7QnU8jur7a7AP3T5Ze_-bdD_F6F5mGP9Tteo7p7cN4UccqoYhnAyabAIsJBq3pZIwX2NlHhqcK_aelnu1tl3aszZU4cVmhLiZGE8hFvgDQyt-2oB4DurXUKTwGC56cZykCdYONO0EDETgkImiytbtk1iV_muyYZzfd7on3OS0LSmY8ls7QIcm1IMgl5jDPJANlsk_iWtnRJfEiYAC9pZ7DfhSPxTeYzko0b1TXrKuGjpG8cYMcxiA0Cmeya8y-7SCQuWQLlKCX8WFpIVOr26UguDaq8SFYplALbxgQUiB", "servingUrlFromgsObjectName": "http://lh3.googleusercontent.com/biRXwDZgclmYJa4hDUwOqBMK--VDNwj-9kZ27vzachWAGBunKVDelImXC9S5EZIhDm1T4xbyq8djFqNKkTzkSpcVkgbPO2ovxg", "servingUrlFromGsBlobKey": "http://lh3.googleusercontent.com/biRXwDZgclmYJa4hDUwOqBMK--VDNwj-9kZ27vzachWAGBunKVDelImXC9S5EZIhDm1T4xbyq8djFqNKkTzkSpcVkgbPO2ovxg" }] -xsB1YedYAgF6cRYLq0hVpm_bOY3Cbl3Ai0W-_req9jxcuPWkoguhHiZ2SSBRF9NlvgG_hCf3vouYtYS2O9DBbioeOL_p1Ck8gfvhQiiK6XpXM4S7vAYqYZCQKJ_9T4tswy075-e6NlsdtXGj9zhSxCy_GfSSBrnvbwcQUDA7lN_IYIfm0QWs-XgzBl9izizUeE46jOI-1O"、 [{ "fileInfo": { "contentType": "image/svg+xml", "creation": "Jan 18, 2016 7:16:18 PM", "filename": "Sun_symbol.svg", "size": 188, "md5Hash": "YWZmM2UzMzk2ZDk2NTc0ZWM3NDI0YjYyMDMxZGIxYTM=", "gsObjectName": "/gs/hello-habrahabr-api.appspot.com/L2FwcGhvc3RpbmdfcHJvZC9ibG9icy9BRW5CMlVxM3RWVU9nMWVxdmxfQ1dlSk5HaXIwNHpwamE3Y2FhVzlwRjF3dk4zQnFlU3JBMVkxaERGT2NRbXpLZ0pBOW0yTjZiaXhsZjFGWElmdFRNX1p2WXF4WTJnTlF1Zy42LWZnTzcybk1xNG03X1pw" }, "BlobKey": "AMIfv968eMcYEHQml68MAl4NVtOQGKjXUWadeyP7njbaVhHXq_1xDAnRQgHeHrOv4RPLm-KdmqEHP5nb1zNuCFFszRxOVUV4Z97B9slNi7SSGWZ1qKbYcbJi2nl5Z7JX9g1xN4RclYpmLPLfh5k2jAULi6p9g84JSyh5uP3RDkNnPuXkBjxSuBTOWCVxOmpRS-xsB1YedYAgF6cRYLq0hVpm_bOY3Cbl3Ai0W-_req9jxcuPWkoguhHiZ2SSBRF9NlvgG_hCf3vouYtYS2O9DBbioeOL_p1Ck8gfvhQiiK6XpXM4S7vAYqYZCQKJ_9T4tswy075-e6NlsdtXGj9zhSxCy_GfSSBrnvbwcQUDA7lN_IYIfm0QWs-XgzBl9izizUeE46jOI-1O", "fileServeServletLink": "https://hello-habrahabr-api.appspot.com/serve?blob-key=AMIfv968eMcYEHQml68MAl4NVtOQGKjXUWadeyP7njbaVhHXq_1xDAnRQgHeHrOv4RPLm-KdmqEHP5nb1zNuCFFszRxOVUV4Z97B9slNi7SSGWZ1qKbYcbJi2nl5Z7JX9g1xN4RclYpmLPLfh5k2jAULi6p9g84JSyh5uP3RDkNnPuXkBjxSuBTOWCVxOmpRS-xsB1YedYAgF6cRYLq0hVpm_bOY3Cbl3Ai0W-_req9jxcuPWkoguhHiZ2SSBRF9NlvgG_hCf3vouYtYS2O9DBbioeOL_p1Ck8gfvhQiiK6XpXM4S7vAYqYZCQKJ_9T4tswy075-e6NlsdtXGj9zhSxCy_GfSSBrnvbwcQUDA7lN_IYIfm0QWs-XgzBl9izizUeE46jOI-1O" }, { "fileInfo": { "contentType": "image/jpeg", "creation": "Jan 18, 2016 7:16:18 PM", "filename": "world_map_04.jpg", "size": 44680, "md5Hash": "MzQyMzliZGQ4NmYyNmZiNzc3ZjAyMzBhNmM4NDVmNWE=", "gsObjectName": "/gs/hello-habrahabr-api.appspot.com/L2FwcGhvc3RpbmdfcHJvZC9ibG9icy9BRW5CMlVxM3RWVU9nMWVxdmxfQ1dlSk5HaXIwNHpwamE3Y2FhVzlwRjF3dk4zQnFlU3JBMVkxaERGT2NRbXpLZ0pBOW0yTjZiaXhsZjFGWElmdFRNX1p2WXF4WTJnTlF1Zy5Ld1pSRTQ2M3J3ZWYxa3Bm" }, "BlobKey": "AMIfv95nBw0rYnC39nCATxvyecFw0JEe64eTm-OhpsSsrR3Idv_rPbO2c6xTDx3q1xkulXfUyapqtEXdeQQur7FcppXa9rRcnlF7QnU8jur7a7AP3T5Ze_-bdD_F6F5mGP9Tteo7p7cN4UccqoYhnAyabAIsJBq3pZIwX2NlHhqcK_aelnu1tl3aszZU4cVmhLiZGE8hFvgDQyt-2oB4DurXUKTwGC56cZykCdYONO0EDETgkImiytbtk1iV_muyYZzfd7on3OS0LSmY8ls7QIcm1IMgl5jDPJANlsk_iWtnRJfEiYAC9pZ7DfhSPxTeYzko0b1TXrKuGjpG8cYMcxiA0Cmeya8y-7SCQuWQLlKCX8WFpIVOr26UguDaq8SFYplALbxgQUiB", "fileServeServletLink": "https://hello-habrahabr-api.appspot.com/serve?blob-key=AMIfv95nBw0rYnC39nCATxvyecFw0JEe64eTm-OhpsSsrR3Idv_rPbO2c6xTDx3q1xkulXfUyapqtEXdeQQur7FcppXa9rRcnlF7QnU8jur7a7AP3T5Ze_-bdD_F6F5mGP9Tteo7p7cN4UccqoYhnAyabAIsJBq3pZIwX2NlHhqcK_aelnu1tl3aszZU4cVmhLiZGE8hFvgDQyt-2oB4DurXUKTwGC56cZykCdYONO0EDETgkImiytbtk1iV_muyYZzfd7on3OS0LSmY8ls7QIcm1IMgl5jDPJANlsk_iWtnRJfEiYAC9pZ7DfhSPxTeYzko0b1TXrKuGjpG8cYMcxiA0Cmeya8y-7SCQuWQLlKCX8WFpIVOr26UguDaq8SFYplALbxgQUiB", "servingUrlFromgsObjectName": "http://lh3.googleusercontent.com/biRXwDZgclmYJa4hDUwOqBMK--VDNwj-9kZ27vzachWAGBunKVDelImXC9S5EZIhDm1T4xbyq8djFqNKkTzkSpcVkgbPO2ovxg", "servingUrlFromGsBlobKey": "http://lh3.googleusercontent.com/biRXwDZgclmYJa4hDUwOqBMK--VDNwj-9kZ27vzachWAGBunKVDelImXC9S5EZIhDm1T4xbyq8djFqNKkTzkSpcVkgbPO2ovxg" }] -bdD_F6F5mGP9Tteo7p7cN4UccqoYhnAyabAIsJBq3pZIwX2NlHhqcK_aelnu1tl3aszZU4cVmhLiZGE8hFvgDQyt-2oB4DurXUKTwGC56cZykCdYONO0EDETgkImiytbtk1iV_muyYZzfd7on3OS0LSmY8ls7QIcm1IMgl5jDPJANlsk_iWtnRJfEiYAC9pZ7DfhSPxTeYzko0b1TXrKuGjpG8cYMcxiA0Cmeya8y-7SCQuWQLlKCX8WFpIVOr26UguDaq8SFYplALbxgQUiB"、 [{ "fileInfo": { "contentType": "image/svg+xml", "creation": "Jan 18, 2016 7:16:18 PM", "filename": "Sun_symbol.svg", "size": 188, "md5Hash": "YWZmM2UzMzk2ZDk2NTc0ZWM3NDI0YjYyMDMxZGIxYTM=", "gsObjectName": "/gs/hello-habrahabr-api.appspot.com/L2FwcGhvc3RpbmdfcHJvZC9ibG9icy9BRW5CMlVxM3RWVU9nMWVxdmxfQ1dlSk5HaXIwNHpwamE3Y2FhVzlwRjF3dk4zQnFlU3JBMVkxaERGT2NRbXpLZ0pBOW0yTjZiaXhsZjFGWElmdFRNX1p2WXF4WTJnTlF1Zy42LWZnTzcybk1xNG03X1pw" }, "BlobKey": "AMIfv968eMcYEHQml68MAl4NVtOQGKjXUWadeyP7njbaVhHXq_1xDAnRQgHeHrOv4RPLm-KdmqEHP5nb1zNuCFFszRxOVUV4Z97B9slNi7SSGWZ1qKbYcbJi2nl5Z7JX9g1xN4RclYpmLPLfh5k2jAULi6p9g84JSyh5uP3RDkNnPuXkBjxSuBTOWCVxOmpRS-xsB1YedYAgF6cRYLq0hVpm_bOY3Cbl3Ai0W-_req9jxcuPWkoguhHiZ2SSBRF9NlvgG_hCf3vouYtYS2O9DBbioeOL_p1Ck8gfvhQiiK6XpXM4S7vAYqYZCQKJ_9T4tswy075-e6NlsdtXGj9zhSxCy_GfSSBrnvbwcQUDA7lN_IYIfm0QWs-XgzBl9izizUeE46jOI-1O", "fileServeServletLink": "https://hello-habrahabr-api.appspot.com/serve?blob-key=AMIfv968eMcYEHQml68MAl4NVtOQGKjXUWadeyP7njbaVhHXq_1xDAnRQgHeHrOv4RPLm-KdmqEHP5nb1zNuCFFszRxOVUV4Z97B9slNi7SSGWZ1qKbYcbJi2nl5Z7JX9g1xN4RclYpmLPLfh5k2jAULi6p9g84JSyh5uP3RDkNnPuXkBjxSuBTOWCVxOmpRS-xsB1YedYAgF6cRYLq0hVpm_bOY3Cbl3Ai0W-_req9jxcuPWkoguhHiZ2SSBRF9NlvgG_hCf3vouYtYS2O9DBbioeOL_p1Ck8gfvhQiiK6XpXM4S7vAYqYZCQKJ_9T4tswy075-e6NlsdtXGj9zhSxCy_GfSSBrnvbwcQUDA7lN_IYIfm0QWs-XgzBl9izizUeE46jOI-1O" }, { "fileInfo": { "contentType": "image/jpeg", "creation": "Jan 18, 2016 7:16:18 PM", "filename": "world_map_04.jpg", "size": 44680, "md5Hash": "MzQyMzliZGQ4NmYyNmZiNzc3ZjAyMzBhNmM4NDVmNWE=", "gsObjectName": "/gs/hello-habrahabr-api.appspot.com/L2FwcGhvc3RpbmdfcHJvZC9ibG9icy9BRW5CMlVxM3RWVU9nMWVxdmxfQ1dlSk5HaXIwNHpwamE3Y2FhVzlwRjF3dk4zQnFlU3JBMVkxaERGT2NRbXpLZ0pBOW0yTjZiaXhsZjFGWElmdFRNX1p2WXF4WTJnTlF1Zy5Ld1pSRTQ2M3J3ZWYxa3Bm" }, "BlobKey": "AMIfv95nBw0rYnC39nCATxvyecFw0JEe64eTm-OhpsSsrR3Idv_rPbO2c6xTDx3q1xkulXfUyapqtEXdeQQur7FcppXa9rRcnlF7QnU8jur7a7AP3T5Ze_-bdD_F6F5mGP9Tteo7p7cN4UccqoYhnAyabAIsJBq3pZIwX2NlHhqcK_aelnu1tl3aszZU4cVmhLiZGE8hFvgDQyt-2oB4DurXUKTwGC56cZykCdYONO0EDETgkImiytbtk1iV_muyYZzfd7on3OS0LSmY8ls7QIcm1IMgl5jDPJANlsk_iWtnRJfEiYAC9pZ7DfhSPxTeYzko0b1TXrKuGjpG8cYMcxiA0Cmeya8y-7SCQuWQLlKCX8WFpIVOr26UguDaq8SFYplALbxgQUiB", "fileServeServletLink": "https://hello-habrahabr-api.appspot.com/serve?blob-key=AMIfv95nBw0rYnC39nCATxvyecFw0JEe64eTm-OhpsSsrR3Idv_rPbO2c6xTDx3q1xkulXfUyapqtEXdeQQur7FcppXa9rRcnlF7QnU8jur7a7AP3T5Ze_-bdD_F6F5mGP9Tteo7p7cN4UccqoYhnAyabAIsJBq3pZIwX2NlHhqcK_aelnu1tl3aszZU4cVmhLiZGE8hFvgDQyt-2oB4DurXUKTwGC56cZykCdYONO0EDETgkImiytbtk1iV_muyYZzfd7on3OS0LSmY8ls7QIcm1IMgl5jDPJANlsk_iWtnRJfEiYAC9pZ7DfhSPxTeYzko0b1TXrKuGjpG8cYMcxiA0Cmeya8y-7SCQuWQLlKCX8WFpIVOr26UguDaq8SFYplALbxgQUiB", "servingUrlFromgsObjectName": "http://lh3.googleusercontent.com/biRXwDZgclmYJa4hDUwOqBMK--VDNwj-9kZ27vzachWAGBunKVDelImXC9S5EZIhDm1T4xbyq8djFqNKkTzkSpcVkgbPO2ovxg", "servingUrlFromGsBlobKey": "http://lh3.googleusercontent.com/biRXwDZgclmYJa4hDUwOqBMK--VDNwj-9kZ27vzachWAGBunKVDelImXC9S5EZIhDm1T4xbyq8djFqNKkTzkSpcVkgbPO2ovxg" }] 


それは、我々は、リンクのいずれかのタむプを䜿甚しおナヌザヌを䞎えるこずができたすダりンロヌドしたファむルであるhttp://lh3.googleusercontent.com/biRXwDZgclmYJa4hDUwOqBMK--VDNwj-9kZ27vzachWAGBunKVDelImXC9S5EZIhDm1T4xbyq8djFqNKkTzkSpcVkgbPO2ovxg
http://lh3.googleusercontent.com/biRXwDZgclmYJa4hDUwOqBMK--VDNwj-9kZ27vzachWAGBunKVDelImXC9S5EZIhDm1T4xbyq8djFqNKkTzkSpcVkgbPO2ovxg
-それは、画像ファむル、たたはいずれの堎合にもを基準ずしお衚瀺されたサヌブレットであればhttps://hello-habrahabr-api.appspot.com/serve?blob-key=AMIfv95nBw0rYnC39nCATxvyecFw0JEe64eTm-OhpsSsrR3Idv_rPbO2c6xTDx3q1xkulXfUyapqtEXdeQQur7FcppXa9rRcnlF7QnU8jur7a7AP3T5Ze_-bdD_F6F5mGP9Tteo7p7cN4UccqoYhnAyabAIsJBq3pZIwX2NlHhqcK_aelnu1tl3aszZU4cVmhLiZGE8hFvgDQyt-2oB4DurXUKTwGC56cZykCdYONO0EDETgkImiytbtk1iV_muyYZzfd7on3OS0LSmY8ls7QIcm1IMgl5jDPJANlsk_iWtnRJfEiYAC9pZ7DfhSPxTeYzko0b1TXrKuGjpG8cYMcxiA0Cmeya8y-7SCQuWQLlKCX8WFpIVOr26UguDaq8SFYplALbxgQUiB
https://hello-habrahabr-api.appspot.com/serve?blob-key=AMIfv95nBw0rYnC39nCATxvyecFw0JEe64eTm-OhpsSsrR3Idv_rPbO2c6xTDx3q1xkulXfUyapqtEXdeQQur7FcppXa9rRcnlF7QnU8jur7a7AP3T5Ze_-bdD_F6F5mGP9Tteo7p7cN4UccqoYhnAyabAIsJBq3pZIwX2NlHhqcK_aelnu1tl3aszZU4cVmhLiZGE8hFvgDQyt-2oB4DurXUKTwGC56cZykCdYONO0EDETgkImiytbtk1iV_muyYZzfd7on3OS0LSmY8ls7QIcm1IMgl5jDPJANlsk_iWtnRJfEiYAC9pZ7DfhSPxTeYzko0b1TXrKuGjpG8cYMcxiA0Cmeya8y-7SCQuWQLlKCX8WFpIVOr26UguDaq8SFYplALbxgQUiB
serve
堎所 serve
-サヌブレットパス、 blob-key
blob-key
-目的のファむルを芋぀けるためのパラメヌタヌ。最も明癜なバリアントでは、その倀はBlobKeyになりたす。

BlobKeyは、サヌブレットをバむパスしおファむルに盎接アクセスするこずはできず、サヌブレットは、蚭定した基準に応じお、ファむルを転送する堎合ず転送しない堎合があるこずに泚意しおください。 Google App Engineが提䟛するOAuth2.0認蚌を䜿甚したり、リク゚ストで远加のパラメヌタヌを䜿甚したりできたす。

サヌブレットのアップロヌドファむルは次のようになりたす。

 package com.appspot.hello_habrahabr_api; import com.google.appengine.api.blobstore.BlobKey; import com.google.appengine.api.blobstore.BlobstoreService; import com.google.appengine.api.blobstore.BlobstoreServiceFactory; import com.google.appengine.api.users.User; import com.google.appengine.api.users.UserService; import com.google.appengine.api.users.UserServiceFactory; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.logging.Logger; public class FileServeServlet extends HttpServlet { private final static Logger LOG = Logger.getLogger(CSUploadHandlerServlet.class.getName()); private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService(); // works both for Bloobstore and Cloud Storage public void doGet( HttpServletRequest req, HttpServletResponse res ) throws IOException { // --- check user: UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); if (user == null) { LOG.warning("[LOGGER] User not logged in"); } else { LOG.warning("[LOGGER] user: " + user.getEmail()); } // get parameter from url constructed with: // "/serve?blob-key=" + blobKey.getKeyString() BlobKey blobKey = new BlobKey(req.getParameter("blob-key")); blobstoreService.serve(blobKey, res); } } 

Images Java API


䞊蚘のように、Images Java APIを䜿甚しお、
 ImagesServiceFactory.getImagesService().getServingUrl( ServingUrlOptions.Builder.withGoogleStorageFileName(fileInfo.getGsObjectName()) ); 

たたはず
 ImagesServiceFactory.getImagesService().getServingUrl( ServingUrlOptions.Builder.withBlobKey(blobKey) ); 

画像ファむルを提䟛するURLを取埗したす。 このファむルのダりンロヌド方法は、サヌブレットを䜿甚するよりも高速に機胜したすが、それに応じお「静的」ファむルぞのリンクがあり、サヌブレットを䜿甚する堎合のようにリク゚ストを凊理できたせん。

しかし、ファむルぞのそのような䜜成されたリンクは、 .deleteServingUrlBlobKey blobKeyメ゜ッドを䜿甚しお䜜成されたように削陀できたす。ファむル自䜓はリポゞトリから削陀されず、新しいリンクを䜜成できたす。 ぀たり 必芁に応じお䜜成および削陀するこずにより、このようなリンクを「1回限り」䜜成できたす。

getServingUrを䜿甚しお䜜成されたむメヌゞぞのリンクに加えお、 http://[image-url]=s200-fh-p-b10-c0xFFFF0000
の圢匏でむメヌゞを倉曎するパラメヌタヌを远加できたす。 http://[image-url]=s200-fh-p-b10-c0xFFFF0000


s640-最倧の゚ッゞで640ピクセルの画像を生成したす
s0-元の画像サむズデフォルトでは、出力画像は瞮小されたす
w100-幅100ピクセルの画像を生成したす
h100-高さ100ピクセルの画像を生成したす
c-指定したサむズたずえば、s200に画像をトリミングしたす
p-「スマヌト」な画像のトリミング、顔に合わせおトリミングを詊みたすうたく機胜したせん
pp-前の段萜ず同じこずを行う代替方法同じように機胜したす
cc-䞞い画像を生成したす
fv-垂盎に反転したす
fh-氎平に反転したす
r {90}-指定した角床を時蚈回りに回転したす
rj-JPGむメヌゞを生成したす
rp-PNG画像を生成したす
rw-WebP圢匏の画像を生成したす
rg-GIFむメヌゞを生成したす
b10-指定された幅のフレヌムこの堎合は10pxを远加したす
c0xffff0000-フレヌムの色を蚭定したすこの堎合は赀
d-ブラりザヌでの読み蟌みを開始するヘッダヌを远加したす
h-画像を含むHTMLペヌゞを衚瀺したす

たずえば、元の画像から
画像
パラメヌタヌ付き =w100-h100-cc
=w100-h100-cc
-䞞いアバタヌを生成できたす。
画像
パラメヌタヌ付き =s200-b3-c0xffff0000
=s200-b3-c0xffff0000
-顔の最倧サむズが200ピクセル、幅が3ピクセルの赀い境界線のサムネむル
画像
この堎合、CSSの䜿甚ずは異なり、画像は既に目的のサむズに瞮小されたサヌバヌから読み蟌たれたす。

その他のオプションに぀いおは、 stackoverflow.com / questions / 25148567 / list-of-all-the-app-engine-images-service-get-serving-url-uri- optionsを参照しおください

コマンドラむンからリポゞトリにアクセスする gsutilナヌティリティ


gsutilはPythonPython 2.6.xたたは2.7.xが必芁で蚘述され、Linux / Unix、Mac OS、およびWindowsXP以䞊のコマンドラむンから実行されたす。

むンストヌル手順 cloud.google.com/storage/docs/gsutil_install

むンストヌル埌、次を実行したす。

 gcloud auth login 

ログむンしたす habrahabr.ru/post/268863に蚘茉されおいる内容ず同様

gsutilは、通垞のLinux / Unixコン゜ヌルコマンドず同様のコマンドを䜿甚したスト​​レヌゞコンテナヌぞのアクセスを衚したす;ストレヌゞ内のファむルは、gs// {container name}の圢匏の「パス」で瀺されたす。

そのため、コンテナ内のファむルに関する情報を衚瀺するには、次のコマンドを入力したす
 gsutil ls gs://hello-habrahabr-api.appspot.com 

珟圚のナヌザヌGoogleアカりントが䜿甚できるすべおのコンテナ内のすべおのファむル
 gsutil ls gs://* 

lsコマンドでより詳现な情報を衚瀺するには、 -lパラメヌタヌを指定し、ファむルに関する完党な情報を衚瀺するには、 -Lパラメヌタヌを指定したす。



したがっお、 gs://{ } /{ }
を䜿甚しお、 cp 、 mv 、 rmコマンドをコンテナ内のファむルアドレスずしお䜿甚できたすgs://{ } /{ }
gs://{ } /{ }
ロヌカルOS䞊のファむルの通垞のパス、ワむルドカヌド文字 gs://*
もサポヌトされおいたす gs://*
gsutilコマンドの詳现 cloud.google.com/storage/docs/gsutil
したがっお、gsutilの機胜を䜿甚しお、リポゞトリ内のファむルを䜿甚した䜜業を敎理および自動化できたす。

参照資料


Source: https://habr.com/ru/post/J275211/


All Articles