Подтвердить что ты не робот

Использование blobstore с конечной точкой Google и Android

Я разрабатываю проект Android с Android-движком, используя плагин eclipse. Один из аспектов приложения - позволить пользователю Alpha отправлять фотографии пользователю Bravo. Для этого у меня есть следующая настройка:

Пользовательская публикация альфа:

  • отправить изображение на сервер приложений через конечные точки
  • сервер хранит изображение в хранилище blob.
  • сервер хранит blobkey в хранилище данных

Пользователь Bravo получает:

  • сервер получает blobkey из хранилища данных
  • сервер получает изображение с помощью клавиши blob
  • сервер отправляет изображение в приложение Android с помощью конечных точек

Эта настройка занимает два (2) минуты, когда мое приложение Android отправляет изображение, когда я вижу его в язве blob. Излишне говорить, что это совершенно неприемлемо.

Мой сервер обрабатывает изображение программно, используя следующий код:

public static BlobKey toBlobstore(Blob imageData) throws FileNotFoundException, FinalizationException, LockException, IOException {
        if (null == imageData)
            return null;

        // Get a file service
        FileService fileService = FileServiceFactory.getFileService();

        // Create a new Blob file with mime-type "image/png"
        AppEngineFile file = fileService.createNewBlobFile("image/jpeg");// png

        // Open a channel to write to it
        boolean lock = true;
        FileWriteChannel writeChannel = fileService.openWriteChannel(file, lock);

        // This time we write to the channel directly
        writeChannel.write(ByteBuffer.wrap
            (imageData.getBytes()));

        // Now finalize
        writeChannel.closeFinally();
        return fileService.getBlobKey(file);
    }

Кто-нибудь знает, как я могу либо приспособить официальный пример для использования конечных точек (в случае, когда я должен использовать экземпляры приложений для приложения), либо использовать getServingUrl (минуя мои экземпляры), чтобы хранить и обслуживать мои капли?
Пожалуйста, вместо слов, введите код. Спасибо.

4b9b3361

Ответ 1

Я расскажу, как я это делаю. Я не использую google-cloud-endpoints, а только собственный собственный api на основе отдыха, но в любом случае это должна быть одна и та же идея.

Я выложу шаг за шагом с кодом, надеюсь, это будет ясно. Вы просто адаптируете способ отправки своих запросов для использования конечных точек вместо того, чтобы делать его более общим, как в этом примере. Я включаю в себя некоторые шаблоны, но исключаю try/catch, проверку ошибок и т.д. Для краткости.

Шаг 1 (клиент)

Первый клиент запрашивает URL-адрес загрузки с сервера:

HttpClient httpclient = new DefaultHttpClient();    
HttpConnectionParams.setConnectionTimeout(httpclient.getParams(), 10000); //Timeout Limit

HttpGet httpGet = new HttpGet("http://example.com/blob/getuploadurl");
response = httpclient.execute(httpGet);

Шаг 2 (сервер)

На стороне сервера сервлет загрузки будет выглядеть примерно так:

String blobUploadUrl = blobstoreService.createUploadUrl("/blob/upload");

res.setStatus(HttpServletResponse.SC_OK);
res.setContentType("text/plain");

PrintWriter out = res.getWriter();
out.print(blobUploadUrl);
out.flush();
out.close();

Обратите внимание на аргумент createUploadUrl. Здесь клиент будет  перенаправляется после завершения фактической загрузки. Что там  вы будете обрабатывать сохранение blobkey и/или обслуживающего URL-адреса и возвращать его клиенту. Вам нужно будет отобразить сервлет на этот URL-адрес, который будет обрабатывать шаг 4

Шаг 3 (клиент) Вернитесь к клиенту еще раз, чтобы отправить фактический файл на URL-адрес загрузки, используя URL-адрес, возвращенный с шага 2.

HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(uploadUrlReturnedFromStep2);

FileBody fileBody  = new FileBody(thumbnailFile);
MultipartEntity reqEntity = new MultipartEntity();

reqEntity.addPart("file", fileBody);

httppost.setEntity(reqEntity);
HttpResponse response = httpclient.execute(httppost)

Как только этот запрос будет отправлен на сервлет на шаге 2, он будет перенаправлен на сервлет, указанный вами в createUploadUrl() ранее

Шаг 4 (сервер)

Назад на сервер: Это сервлет, обрабатывающий URL-адрес, отображаемый на blob/upload. Мы вернем blobkey и обслуживающий URL-адрес клиенту в объекте json:

List<BlobKey> blobs = blobstoreService.getUploads(req).get("file");
BlobKey blobKey = blobs.get(0);

ImagesService imagesService = ImagesServiceFactory.getImagesService();
ServingUrlOptions servingOptions = ServingUrlOptions.Builder.withBlobKey(blobKey);

String servingUrl = imagesService.getServingUrl(servingOptions);

res.setStatus(HttpServletResponse.SC_OK);
res.setContentType("application/json");

JSONObject json = new JSONObject();
json.put("servingUrl", servingUrl);
json.put("blobKey", blobKey.getKeyString());

PrintWriter out = res.getWriter();
out.print(json.toString());
out.flush();
out.close();

Шаг 5 (клиент)

Мы получим blobkey и обслуживающий URL из json, а затем отправим его вместе с идентификатором пользователя и т.д. для хранения в объекте хранилища данных.

JSONObject resultJson = new JSONObject(resultJsonString);

String blobKey = resultJson.getString("blobKey");
String servingUrl = resultJson.getString("servingUrl");

List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);

nameValuePairs.add(new BasicNameValuePair("userId", userId));
nameValuePairs.add(new BasicNameValuePair("blobKey",blobKey));
nameValuePairs.add(new BasicNameValuePair("servingUrl",servingUrl));

HttpClient httpclient = new DefaultHttpClient();
HttpConnectionParams.setConnectionTimeout(httpclient.getParams(), 10000);

HttpPost httppost = new HttpPost(url);
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);

// Continue to store the (immediately available) serving url in local storage f.ex

Шаг 6 (сервер) Фактически сохраняя все в хранилище данных (используя в этом примере объективирование)

final String userId   = req.getParameter("userId");
final String blobKey  = req.getParameter("blobKey");
final String servingUrl = req.getParameter("servingUrl");

ExampleEntity entity = new ExampleEntity();
entity.setUserId(userId);
entity.setBlobKey(blobKey);
entity.setServingUrl(servingUrl);

ofy().save().entity(entity);

Надеюсь, это упростит ситуацию. Если кто-то хочет отредактировать ответ, чтобы использовать конечные точки облака вместо этого более общего примера, не стесняйтесь:)

Об обслуживающем URL

Сервисный URL - отличный способ подавать изображения вашим клиентам, поскольку он может динамически масштабировать изображения на лету. Например, вы можете отправлять меньшие изображения своим пользователям LDPI, просто добавив =sXXX в конец обслуживающего URL-адреса. Где XXX - размер пикселя самого большого размера вашего изображения. Вы полностью избегаете своих экземпляров и платите только за пропускную способность, а пользователь загружает только то, что ей нужно.

PS

На шаге 4 нужно остановиться и просто сохранить его прямо там, пройдя по userId f.ex на шаге 3. Любые параметры должны быть отправлены на шаг 4, но я не получил этого, чтобы работать, так вот как я это делаю в данный момент, поэтому я делюсь этим так, потому что знаю, что это работает.

Ответ 2

Я использовал ответ на этот вопрос для создания собственной системы, использующей конечные точки AppEngine. В отличие от сообщений выше, я хочу иметь чистый API, который напрямую передает изображение (как массив байтов) в конечную точку Google, а загрузка в BlobstorageService выполняется на стороне сервера. Преимущество этого в том, что у меня есть атомный API. Недостатком, очевидно, является загрузка на сервер, а также тяжелые операции сортировки на клиенте.

Android - загрузка, масштабирование и сериализация изображения и загрузка в конечные точки

void uploadImageBackground(Bitmap bitmap) throws IOException {
    // Important! you wanna rescale your bitmap (e.g. with Bitmap.createScaledBitmap)
    // as with full-size pictures the base64 representation would not fit in memory

    // encode bitmap into byte array (very resource-wasteful!)
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
    byte[] byteArray = stream.toByteArray();
    bitmap.recycle();
    bitmap = null;
    stream = null;

    // Note: We encode ourselves, instead of using image.encodeImageData, as this would throw
    //       an 'Illegal character '_' in base64 content' exception
    // See: http://stackoverflow.com/questions/22029170/upload-photos-from-android-app-to-google-cloud-storage-app-engine-illegal-char
    String base64 = Base64.encodeToString(byteArray, Base64.DEFAULT);
    byteArray = null;

    // Upload via AppEngine Endpoint (ImageUploadRequest is a generated model)
    ImageUploadRequest image = new ImageUploadRequest();
    image.setImageData(base64);
    image.setFileName("picture.png");
    image.setMimeType("image/png");
    App.getMyApi().setImage(image).execute();
}

Конечная точка API Backend - Загрузить изображение в BlobstorageService

@ApiMethod(
        name = "setImage",
        path = "setImage",
        httpMethod = ApiMethod.HttpMethod.POST
)
public void saveFoodImageForUser(ImageUploadRequest imageRequest) throws IOException {
    assertNotEmpty(userId, "userId");
    assertNotNull(imageRequest, "imageRequest");

    // create blob url
    BlobstorageService blobService = BlobstoreServiceFactory.getBlobstoreService();
    String uploadUrl = blobService.createUploadUrl("/blob/upload");

    // create multipart body containing file
    HttpEntity requestEntity = MultipartEntityBuilder.create()
            .addBinaryBody("file", imageRequest.getImageData(),
                    ContentType.create(imageRequest.getMimeType()), imageRequest.getFileName())
            .build();

    // Post request to BlobstorageService
    // Note: We cannot use Apache HttpClient, since AppEngine only supports Url-Fetch
    //  See: https://cloud.google.com/appengine/docs/java/sockets/
    URL url = new URL(uploadUrl);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setDoOutput(true);
    connection.setRequestMethod("POST");
    connection.addRequestProperty("Content-length", requestEntity.getContentLength() + "");
    connection.addRequestProperty(requestEntity.getContentType().getName(), requestEntity.getContentType().getValue());
    requestEntity.writeTo(connection.getOutputStream());

    // BlobstorageService will forward to /blob/upload, which returns our json
    String responseBody = IOUtils.toString(connection.getInputStream());

    if(connection.getResponseCode() < 200 || connection.getResponseCode() >= 400) {
        throw new IOException("HTTP Status " + connection.getResponseCode() + ": " + connection.getHeaderFields() + "\n" + responseBody);
    }

    // parse BlopUploadServlet Json response
    ImageUploadResponse response = new Gson().fromJson(responseBody, ImageUploadResponse.class);

    // save blobkey and serving url ...
}

Сервлет, который обрабатывает обратный вызов из BlobstorageService

public class BlobUploadServlet extends HttpServlet {
    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        BlobstorageService blobService = BlobstoreServiceFactory.getBlobstoreService();
        List<BlobKey> blobs = blobService.getUploads(req).get("file");
        if(blobs == null || blobs.isEmpty()) throw new IllegalArgumentException("No blobs given");

        BlobKey blobKey = blobs.get(0);

        ImagesService imagesService = ImagesServiceFactory.getImagesService();
        ServingUrlOptions servingOptions = ServingUrlOptions.Builder.withBlobKey(blobKey);

        String servingUrl = imagesService.getServingUrl(servingOptions);

        res.setStatus(HttpServletResponse.SC_OK);
        res.setContentType("application/json");

        // send simple json response (ImageUploadResponse is a POJO)
        ImageUploadResponse result = new ImageUploadResponse();
        result.setBlobKey(blobKey.getKeyString());
        result.setServingUrl(servingUrl);

        PrintWriter out = res.getWriter();
        out.print(new Gson().toJson(result));
        out.flush();
        out.close();
    }
}

Осталось только привязать /blob/upload к UploadBlobServlet.

Примечание. Это не работает, когда AppEngine работает локально (если выполняется локально, тогда POST в BlobstorageService всегда будет возвращать 404 NOT FOUND)

Ответ 3

Поскольку я пробовал много способов сделать службу обратного вызова в api конечной точки, я отменяю этот aproach. Однако я мог бы решить эту проблему, сделав параллельный сервлет конечной точке api, ему нужно только определить сервер класса и добавить его в конфигурацию web.xml. Здесь мое решение:

1 Служба Enpoint для получения URL для загрузки: Затем служебный coudl должен быть защищен clientId

@ApiMethod(name = "getUploadURL",  httpMethod = HttpMethod.GET)
    public Debug getUploadURL() { 
        String blobUploadUrl =  blobstoreService.createUploadUrl("/update");
        Debug debug = new Debug(); 
        debug.setData(blobUploadUrl);
        return debug; 
    }

2. Теперь клиент может позвонить в конечную точку для получения URL-адреса для загрузки:
Может быть, некоторые вроде этого (для android тоже используйте вашу клиентскую библиотеку):

gapi.client.debugendpoint.getUploadURL().execute(); 

3. Следующим шагом будет toto сообщение для URL, зашедшего на последнем шаге: Вы можете сделать это с помощью httpClient android, опять же, в моем случае мне нужно загрузить из Интернета, затем я использую форму и обратный вызов события onChangeFile() для получения uploadurl (используя шаг 3), затем, когда он отвечает на изменение формы параметры "действие" и "codeId" перед тем, как кто-то решит нажать кнопку "Отправить":

<form id="submitForm"  action="put_here_uploadUrl" method="post" enctype="multipart/form-data">
<input type="file" name="image" onchange="onChangeFile()">
<input type="text" name="codeId" value='put_here_some_dataId'>
<input type="submit" value="Submit"></form>

4 Наконец, класс сервлетов paralele:

@SuppressWarnings("serial")
public class Update  extends HttpServlet{

    public void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {    

        String userId   = req.getParameter("codeId");

        List<BlobKey> blobs = BSF.getService().getUploads(req).get("image");
        BlobKey blobKey = blobs.get(0);

        ImagesService imagesService = ImagesServiceFactory.getImagesService();
        ServingUrlOptions servingOptions = ServingUrlOptions.Builder.withBlobKey(blobKey);
        String servingUrl = imagesService.getServingUrl(servingOptions);

        resp.setStatus(HttpServletResponse.SC_OK);
        resp.setContentType("application/json");


        JSONObject json = new JSONObject();
        try {
            json.put("imageUrl", servingUrl);
            json.put("codeId", "picture_of_"+userId);
            json.put("blobKey",  blobKey.getKeyString());
        } catch (JSONException e){

            e.printStackTrace();            
        }

        PrintWriter out = resp.getWriter();
        out.print(json.toString());
        out.flush();
        out.close();
    }
}

и добавьте в web.xml, где com.apppack - пакет обновления класса

<servlet>
<servlet-name>update</servlet-name>
<servlet-class>com.apppack.Update</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>update</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>