【问题标题】:Send image data to android app from App Engine从 App Engine 向 Android 应用发送图像数据
【发布时间】:2016-05-15 19:56:42
【问题描述】:

在我的 App Engine 后端,我有一个从 Google Cloud Storage 获取图像的方法

@ApiMethod(
        name = "getProfileImage",
        path = "image",
        httpMethod = ApiMethod.HttpMethod.GET)
public Image getProfileImage(@Named("imageName")String imageName){
    try{
        HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        GoogleCredential credential = GoogleCredential.getApplicationDefault();

        Storage.Builder storageBuilder = new Storage.Builder(httpTransport,new JacksonFactory(),credential);
        Storage storage = storageBuilder.build();

        Storage.Objects.Get getObject = storage.objects().get("mybucket", imageName);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        // If you're not in AppEngine, download the whole thing in one request, if possible.
        getObject.getMediaHttpDownloader().setDirectDownloadEnabled(false);
        getObject.executeMediaAndDownloadTo(out);

        byte[] oldImageData = out.toByteArray();
        out.close();

        ImagesService imagesService = ImagesServiceFactory.getImagesService();

        return ImagesServiceFactory.makeImage(oldImageData);
    }catch(Exception e){
        logger.info("Error getting image named "+imageName);
    }
    return null;
}

我遇到的问题是当我在我的 android 应用程序中调用它时如何获取图像数据?

由于您无法从应用引擎返回原语,我将其转换为 Image,以便我可以在我的应用中调用 getImageData() 来获取字节[]。

但是返回给应用的Image对象与应用引擎中的不同,所以没有getImageData()。

如何将图像数据获取到我的 Android 应用程序?

如果我创建了一个包含 byte[] 变量的对象,那么我使用字符串数据设置 byte[] 变量并从方法中返回该对象,这样行吗?

更新

图片是从安卓应用发送的。 (这段代码可能正确也可能不正确,我还没有调试过)

@WorkerThread
    public String startResumableSession(){
        try{
            File file = new File(mFilePath);
            long fileSize = file.length();
            file = null;
            String sUrl = "https://www.googleapis.com/upload/storage/v1/b/lsimages/o?uploadType=resumable&name="+mImgName;
            URL url = new URL(sUrl);
            HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
            urlConnection.setRequestProperty("Authorization","");
            urlConnection.setRequestProperty("X-Upload-Content-Type","image/png");
            urlConnection.setRequestProperty("X-Upload-Content-Length",String.valueOf(fileSize));
            urlConnection.setRequestMethod("POST");

            if(urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK){
                return urlConnection.getHeaderField("Location");
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private long sendNextChunk(String sUrl,File file,long skip){
        int bytesRead, bytesAvailable, bufferSize;
        byte[] buffer;
        int maxBufferSize = 524287;
        long totalBytesSent = 0;
        try{
            long fileSize = file.length();
            FileInputStream fileInputStream = new FileInputStream(file);
            skip = fileInputStream.skip(skip);

            bytesAvailable = fileInputStream.available();
            bufferSize = Math.min(bytesAvailable, maxBufferSize);
            totalBytesSent = skip + bufferSize;
            buffer = new byte[bufferSize];

            bytesRead = fileInputStream.read(buffer, 0, bufferSize);
            try {
                while (bytesRead > 0) {

                    try {
                        URL url = new URL(sUrl);
                        HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
                        urlConnection.setDoInput(true);
                        urlConnection.setDoOutput(true);
                        urlConnection.setUseCaches(false);
                        urlConnection.setChunkedStreamingMode(524287);
                        urlConnection.setRequestMethod("POST");
                        urlConnection.setRequestProperty("Connection", "Keep-Alive");
                        urlConnection.setRequestProperty("Content-Type","image/png");
                        urlConnection.setRequestProperty("Content-Length",String.valueOf(bytesRead));
                        urlConnection.setRequestProperty("Content-Range", "bytes "+String.valueOf(skip)+"-"+String.valueOf(totalBytesSent)+"/"+String.valueOf(fileSize));

                        DataOutputStream outputStream = new DataOutputStream(urlConnection.getOutputStream());
                        outputStream.write(buffer, 0, bufferSize);

                        int code = urlConnection.getResponseCode();

                        if(code == 308){
                            String range = urlConnection.getHeaderField("Range");
                            return Integer.parseInt(range.split("-")[1]);
                        }else if(code == HttpURLConnection.HTTP_CREATED){
                            return -1;
                        }

                        outputStream.flush();
                        outputStream.close();
                        outputStream = null;
                    } catch (OutOfMemoryError e) {
                        e.printStackTrace();
//                        response = "outofmemoryerror";
//                        return response;
                        return -1;
                    }
                    fileInputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
//                response = "error";
//                return response;
                return -1;
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return -1;
    }

编辑 2:

显然人们不清楚我在我的 android 应用程序中使用 Endpoints

【问题讨论】:

  • 我通常使用 Python,所以我不知道您在 Java 中这样做,但您可以只返回从 Google Cloud Storage 生成的个人资料图片 url,而不是返回字节数组。这样,您可以缓存配置文件 url 以避免一直重建它。
  • 正如@Maël 所说,您不应该通过堆栈传输整个图像(出于性能原因,同时也是为了降低成本)。你创建图像类型吗?如果是,您可以发布其来源吗?
  • @BenoîtSauvère 图片来自 android 应用程序,我已使用该代码更新了问题。要获取图像 URL,我是否必须下载图像数据?所以基本上我会下载两次图像数据,一次在服务器上只是为了获取服务 URL,另一次在客户端应用程序上。这不会比从服务器获取图像数据效率低(成本)吗?一旦 android 应用程序获取图像数据,我的计划是在本地缓存图像(将其保存到磁盘),这样它就不必再次获取它
  • @Maël 图像数据只会下载一次,然后在需要再次显示时将图像存储在设备上。此外,从服务器获取图像数据的想法是向服务器发送图像将要显示的位置的宽度和高度,服务器会对其进行缩放而不是强制客户端这样做

标签: android google-app-engine google-cloud-storage


【解决方案1】:

我最终做了什么/发现您需要在带有端点的 api 调用上调用 execute(),它会返回从 API 传回的真实数据

例子

api调用返回Image

public Image getProfileImage(@Named("id") long id, @Named("imageName")String imageName){
        try{
            ProfileRecord pr = get(id);
            HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
            GoogleCredential credential = GoogleCredential.getApplicationDefault();

            Storage.Builder storageBuilder = new Storage.Builder(httpTransport,new JacksonFactory(),credential);
            Storage storage = storageBuilder.build();

            Storage.Objects.Get getObject = storage.objects().get("mybucket", imageName);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        // If you're not in AppEngine, download the whole thing in one request, if possible.
        getObject.getMediaHttpDownloader().setDirectDownloadEnabled(false);
        getObject.executeMediaAndDownloadTo(out);

        byte[] oldImageData = out.toByteArray();
        out.close();
        return ImagesServiceFactory.makeImage(oldImageData);
    }catch(Exception e){
        logger.info("Error getting image named "+imageName);
    }
    return null;
}

然后在客户端我会这样调用它来获取它

Image i = pr.profileImage(id,"name.jpg").execute();
byte[] data = i.decodeImageData();

【讨论】:

    【解决方案2】:

    您可以为此使用 Google Cloud Endpoints:

    Google Cloud Endpoints 由工具、库和功能组成 允许您从应用程序生成 API 和客户端库 引擎应用程序,称为 API 后端,用于简化客户端 从其他应用程序访问数据。端点更容易 为 Web 客户端和移动客户端创建 Web 后端,例如 Android 或 Apple 的 iOS。

    https://cloud.google.com/appengine/docs/java/endpoints/

    【讨论】:

    • 这是我使用的,所以你没有真正回答我的问题
    • 很抱歉,我帮助您的尝试没有让您满意。也许在您的问题或标签中提及端点可以避免我无法容忍的误解。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-21
    • 2018-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多