Async HTTP Extension with Multipart/Form-Data Support for Imgur Image Upload

Async HTTP Extension with Multipart/Form-Data Support for Imgur Image Upload

This extension is designed for developers who want to upload images to Imgur using asynchronous HTTP POST and GET requests in their apps. The extension supports the multipart/form-data format, making it easy to send media files to the Imgur API.

Features

  • Asynchronous Image Upload: Upload images to Imgur via the multipart/form-data format, ensuring smooth and reliable media uploads.
  • HTTP GET/POST Requests: Supports general HTTP GET and POST requests, providing flexibility for interacting with other APIs beyond Imgur.
  • Event-Driven: The extension triggers events for successful uploads and errors, allowing developers to easily handle these situations within their apps.

How it Works

  • Upload to Imgur: With this extension, you can send image files to Imgur by providing the image path and an authorization token. The upload happens asynchronously in the background, keeping the app responsive.
  • Event Handling: Once the upload is completed, an event is triggered with the response from the server. In case of any errors during the upload process, an error event is triggered, making it easier for developers to manage failures.
  • HTTP Requests: You can also use the extension to make other types of HTTP requests, such as GET or POST, to interact with different web services.

Blocks

imgur_extension02

Requirements

  • A valid Imgur account and authorization token are required to upload images.
  • Ensure that the app has permission to access the storage to upload the desired image files.

Installation

  • Download the extension file (com.bosonshiggs.imgur.aix (10.2 KB))
    and import it into your MIT App Inventor project.
  • Drag and drop the extension component into your app, then configure it as needed.

Contribution

Contributions to improve the extension are always welcome. If you encounter any issues or have suggestions for new features, feel free to submit feedback or open a pull request.

A generic way to use multipart/form-data

package com.bosonshiggs.multipartupload;

import android.os.Handler;
import android.os.Looper;
import com.google.appinventor.components.annotations.*;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.runtime.EventDispatcher;

import org.json.JSONObject;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Iterator;

@DesignerComponent(
        version = 4,
        description = "An extension for making async HTTP POST requests with multipart/form-data and custom headers. Supports optional file upload.",
        category = ComponentCategory.EXTENSION,
        nonVisible = true,
        iconName = "https://your-icon-url-here.png")
@SimpleObject(external = true)
public class MultipartUpload extends AndroidNonvisibleComponent {

    private final Handler uiHandler;
    private static final String LINE_FEED = "\r\n";
    private static final String BOUNDARY = "----WebKitFormBoundary7MA4YWxkTrZu0gW";

    public MultipartUpload(ComponentContainer container) {
        super(container.$form());
        uiHandler = new Handler(Looper.getMainLooper());
    }

    // Método para upload com arquivo
    @SimpleFunction(description = "Sends an asynchronous POST request to upload a file using multipart/form-data with custom headers.")
    public void UploadFile(final String serverUrl, final String filePath, final String authToken, final String headersJson) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    URL url = new URL(serverUrl);
                    HttpURLConnection con = (HttpURLConnection) url.openConnection();
                    con.setRequestMethod("POST");
                    con.setRequestProperty("Authorization", "Bearer " + authToken);
                    con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
                    con.setDoOutput(true);

                    // Adicionar cabeçalhos personalizados
                    JSONObject headers = new JSONObject(headersJson);
                    Iterator<String> keys = headers.keys();
                    while (keys.hasNext()) {
                        String key = keys.next();
                        con.setRequestProperty(key, headers.getString(key));
                    }

                    DataOutputStream outputStream = new DataOutputStream(con.getOutputStream());

                    if (!filePath.isEmpty()) {  // Verifica se o filePath foi fornecido
                        File file = new File(filePath);
                        String fileName = file.getName();
                        outputStream.writeBytes("--" + BOUNDARY + LINE_FEED);
                        outputStream.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"" + LINE_FEED);
                        outputStream.writeBytes("Content-Type: " + HttpURLConnection.guessContentTypeFromName(fileName) + LINE_FEED);
                        outputStream.writeBytes(LINE_FEED);

                        FileInputStream fileInputStream = new FileInputStream(file);
                        byte[] buffer = new byte[4096];
                        int bytesRead = -1;
                        while ((bytesRead = fileInputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                        }
                        fileInputStream.close();

                        outputStream.writeBytes(LINE_FEED);
                    }

                    outputStream.writeBytes("--" + BOUNDARY + "--" + LINE_FEED);
                    outputStream.flush();
                    outputStream.close();

                    BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
                    String inputLine;
                    final StringBuilder response = new StringBuilder();
                    while ((inputLine = in.readLine()) != null) {
                        response.append(inputLine);
                    }
                    in.close();

                    uiHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            FileUploaded(response.toString());
                        }
                    });
                } catch (final Exception e) {
                    uiHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            ErrorOccurred(e.getMessage());
                        }
                    });
                }
            }
        }).start();
    }

    // Método para upload sem arquivo (formulário de dados)
    @SimpleFunction(description = "Sends an asynchronous POST request without file, using multipart/form-data and custom headers.")
    public void UploadWithoutFile(final String serverUrl, final String authToken, final String headersJson) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    URL url = new URL(serverUrl);
                    HttpURLConnection con = (HttpURLConnection) url.openConnection();
                    con.setRequestMethod("POST");
                    con.setRequestProperty("Authorization", "Bearer " + authToken);
                    con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
                    con.setDoOutput(true);

                    // Adicionar cabeçalhos personalizados
                    JSONObject headers = new JSONObject(headersJson);
                    Iterator<String> keys = headers.keys();
                    while (keys.hasNext()) {
                        String key = keys.next();
                        con.setRequestProperty(key, headers.getString(key));
                    }

                    DataOutputStream outputStream = new DataOutputStream(con.getOutputStream());

                    // Somente dados, sem arquivo
                    outputStream.writeBytes("--" + BOUNDARY + "--" + LINE_FEED);
                    outputStream.flush();
                    outputStream.close();

                    BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
                    String inputLine;
                    final StringBuilder response = new StringBuilder();
                    while ((inputLine = in.readLine()) != null) {
                        response.append(inputLine);
                    }
                    in.close();

                    uiHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            FileUploaded(response.toString());  // Mesma função para ambos os métodos
                        }
                    });
                } catch (final Exception e) {
                    uiHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            ErrorOccurred(e.getMessage());
                        }
                    });
                }
            }
        }).start();
    }

    @SimpleEvent(description = "Event triggered when the file upload is successful.")
    public void FileUploaded(String responseContent) {
        EventDispatcher.dispatchEvent(this, "FileUploaded", responseContent);
    }

    @SimpleEvent(description = "Event triggered when an error occurs during the upload process.")
    public void ErrorOccurred(String errorMessage) {
        EventDispatcher.dispatchEvent(this, "ErrorOccurred", errorMessage);
    }
}
9 Likes

You can also modify the Imgur extension and use other services like IMGBB which offers free image upload services.

API Key Registration: https://api.imgbb.com/

package com.bosonshiggs.imgbb;

import android.os.Handler;
import android.os.Looper;
import com.google.appinventor.components.annotations.*;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.runtime.EventDispatcher;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

@DesignerComponent(
        version = 1,
        description = "An extension for making async HTTP POST requests to upload images to ImgBB",
        category = ComponentCategory.EXTENSION,
        nonVisible = true,
        iconName = "https://cdn.iconscout.com/icon/free/png-256/free-imgbb-3244941-2709869.png")
@SimpleObject(external = true)
public class ImgBB extends AndroidNonvisibleComponent {

    private final Handler uiHandler;
    private static final String LINE_FEED = "\r\n";
    private static final String BOUNDARY = "----WebKitFormBoundary7MA4YWxkTrZu0gW";

    public ImgBB(ComponentContainer container) {
        super(container.$form());
        uiHandler = new Handler(Looper.getMainLooper());
    }

    @SimpleFunction(description = "Sends an asynchronous POST request to upload an image file to ImgBB using multipart/form-data.")
    public void UploadImageToImgBB(final String imagePath, final String apiKey) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // Setup URL with the API key
                    URL url = new URL("https://api.imgbb.com/1/upload?key=" + apiKey);
                    HttpURLConnection con = (HttpURLConnection) url.openConnection();
                    con.setRequestMethod("POST");
                    con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
                    con.setDoOutput(true);

                    // Start writing the request body
                    DataOutputStream outputStream = new DataOutputStream(con.getOutputStream());

                    // Add file part
                    File file = new File(imagePath);
                    String fileName = file.getName();
                    outputStream.writeBytes("--" + BOUNDARY + LINE_FEED);
                    outputStream.writeBytes("Content-Disposition: form-data; name=\"image\"; filename=\"" + fileName + "\"" + LINE_FEED);
                    outputStream.writeBytes("Content-Type: " + HttpURLConnection.guessContentTypeFromName(fileName) + LINE_FEED);
                    outputStream.writeBytes(LINE_FEED);

                    // Read file and write to stream
                    FileInputStream fileInputStream = new FileInputStream(file);
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = fileInputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, bytesRead);
                    }
                    fileInputStream.close();

                    outputStream.writeBytes(LINE_FEED);
                    outputStream.writeBytes("--" + BOUNDARY + "--" + LINE_FEED);
                    outputStream.flush();
                    outputStream.close();

                    // Get the response
                    BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
                    String inputLine;
                    final StringBuilder response = new StringBuilder();
                    while ((inputLine = in.readLine()) != null) {
                        response.append(inputLine);
                    }
                    in.close();

                    // Post the result back to the UI thread
                    uiHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            ImageUploaded(response.toString());
                        }
                    });
                } catch (final Exception e) {
                    // Post the error back to the UI thread
                    uiHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            ErrorOccurred(e.getMessage());
                        }
                    });
                }
            }
        }).start();
    }

    @SimpleEvent(description = "Event triggered when the image upload is successful.")
    public void ImageUploaded(String responseContent) {
        EventDispatcher.dispatchEvent(this, "ImageUploaded", responseContent);
    }

    @SimpleEvent(description = "Event triggered when an error occurs during a request.")
    public void ErrorOccurred(String errorMessage) {
        EventDispatcher.dispatchEvent(this, "ErrorOccurred", errorMessage);
    }
}

2 Likes