Функции Azure - Как использовать класс MultipartHttpServletRequest из метода `run ()` по умолчанию? - PullRequest
0 голосов
/ 01 февраля 2019
   public HttpResponseMessage run(
            @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) {

В методе run () спецификации Azure Functions(Java) есть только параметр HttpRequestMessage.Мне нужно объявить и использовать MultipartHttpServletRequest для извлечения файла из запроса multipart / data.Я пытаюсь, но не вижу способа привести HttpRequestMessag к MultipartHttpServletRequest.

Пожалуйста, дайте мне несколько советов.

Спецификация HttpTrigger: https://docs.microsoft.com/en-us/java/api/com.microsoft.azure.functions.annotation.httptrigger?view=azure-java-stable

----------------------- обновление -------------------------

Загруженное изображение все еще повреждено.Размер точно такой же, как и в оригинале, но выглядит так:

enter image description here

Я вставлю весь код.Пожалуйста, просмотрите его.

Источник класса функции:

public class HttpTriggerJava {
    private static final String storageConnectionString =
            "DefaultEndpointsProtocol=http;" +
                    "AccountName=00000;" +
                    "AccountKey=00000";

    @FunctionName("HttpTriggerJava")
    public HttpResponseMessage run(
            @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) throws Exception{

        context.getLogger().info("Java HTTP trigger processed a request.");

        CloudStorageAccount storageAccount = CloudStorageAccount.parse(storageConnectionString);
        CloudBlobClient blobClient = storageAccount.createCloudBlobClient();
        CloudBlobContainer container = blobClient.getContainerReference("contents");

        // here the "content-type" must be lower-case
        String contentType = request.getHeaders().get("content-type"); // Get content-type header

        String body = request.getBody().get(); // Get request body
        String boundary = contentType.split(";")[1].split("=")[1]; // Get boundary from content-type header
        int bufSize = 1024;
        InputStream in = new ByteArrayInputStream(body.getBytes()); // Convert body to an input stream
        MultipartStream multipartStream  = new MultipartStream(in, boundary.getBytes(), bufSize, null); // Using MultipartStream to parse body input stream
        boolean nextPart = multipartStream.skipPreamble();
        while(nextPart) {
            String header = multipartStream.readHeaders();
            System.out.println("");
            System.out.println("Headers:");
            System.out.println(header);
            System.out.println("Body:");
            if (header.contains("Content-Type: image/")) {
                int start = header.indexOf("filename=")+"filename=".length()+1;
                int end = header.indexOf("\r\n")-1;
                String filename = header.substring(start, end);
                System.out.println(filename);
                FileOutputStream fos = new FileOutputStream(filename);
                multipartStream.readBodyData(fos);

                File sourceFile = new File(filename);
                CloudBlockBlob blob = container.getBlockBlobReference(filename);
                blob.uploadFromFile(sourceFile.getAbsolutePath());

            } else {
                multipartStream.readBodyData(System.out);
            }
            System.out.println("");
            nextPart = multipartStream.readBoundary();
        }

        return request.createResponseBuilder(HttpStatus.OK).body("Success").build();
    }
}

И HTML-код:

<head>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$(document).ready(function () {
    $("#myFile").change(function() {
      readURL(this);
    });

    $("#submit").click(function (event) {
        event.preventDefault();

        var form = $('#form')[0];
        var data = new FormData(form);

        $("#submit").prop("disabled", true);

        $.ajax({
            type: "POST",
            enctype: 'multipart/form-data',
            url: $(form).attr('action'),
            data: data,
            processData: false,
            contentType: false,
            cache: false,
            timeout: 600000,
            success: function (data) {
                $("#result").text(data);
                console.log("SUCCESS : ", data);
                $("#submit").prop("disabled", false);
            },
            error: function (e) {
                $("#result").text(e.responseText);
                console.log("ERROR : ", e);
                $("#submit").prop("disabled", false);
            }
        });
    });
});
function readURL(input) {
  if (input.files && input.files[0]) {
    var reader = new FileReader();

    reader.onload = function(e) {
      $('#blah').attr('src', e.target.result).show();
    }
    reader.readAsDataURL(input.files[0]);
  }
}
</script>
</head>

<body>
    <form id=form
        action="http://doopediafunctiontest.azurewebsites.net/api/HttpTriggerJava?code=00000"
        method="post" enctype="multipart/form-data">
        <p>
            <br /> <br /> <strong>My file:</strong><br /> <input type="file" id="myFile" name="myFile">
            <br /><img id="blah" src="#" alt="your image" style="display:none" />
        </p>
        <input id=submit type="submit" value="upload to Blob Storage">
    </form>

    <div id=result></div>
</body>

Я сравниваю исходное изображение и поврежденное изображение с помощью шестнадцатеричного редактора.,И я обнаружил, что некоторые случайные гексы изменились на 3f, это должно быть причиной.Может быть, есть какая-то проблема с кодированием.Но как я могу это исправить?

(нажмите, чтобы увеличить) enter image description here

1 Ответ

0 голосов
/ 04 февраля 2019

Звучит так, будто вы хотите загрузить файл в свою функцию Azure с помощью Http Trigger в Java через HTML-форму с multipart/form-data, как показано ниже.

<form method="POST" enctype="multipart/form-data" action="https://<your function app>/api/HttpTrigger-Java">
  File to upload: <input type="file" name="upfile"><br/>
  Notes about the file: <input type="text" name="note"><br/>
  <br/>
  <input type="submit" value="Press"> to upload the file!
</form>

Однако ни один класс не реализуетинтерфейс HttpRequestMessage<T> и, кажется, не приводит HttpRequestMessage к HttpServletRequest после того, как я исследовал исходный код GitHub Repo Azure/azure-functions-java-library.

По моему опыту, единственный способ - проанализировать заголовок и тело запроса multipart/form-data, чтобы получить файл.Есть ответ на аналогичную ветку SO Библиотека и примеры анализа multipart / form-data из inputtream , опубликованные владельцем вопроса, который включает код с использованием MultipartStream класса Apache Commons FileUpload , который работает после того, как я его протестировал.

Вот заголовок Content-Type и тело запроса multipart/form-data, полученного из функции Azure для Java.

Заголовок Content-Type

content-type: multipart/form-data; boundary=----WebKitFormBoundaryT2TWuevX3RIYWRQF

multipart/form-data тело запроса

------WebKitFormBoundaryT2TWuevX3RIYWRQF
Content-Disposition: form-data; name="upfile"; filename="z.txt"
Content-Type: text/plain
1234
ABCD
------WebKitFormBoundaryT2TWuevX3RIYWRQF
Content-Disposition: form-data; name="note"
test.txt
------WebKitFormBoundaryT2TWuevX3RIYWRQF--

Вот мой пример кода для извлечения файла.

@FunctionName("HttpTrigger-Java")
public HttpResponseMessage run(
        @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
        final ExecutionContext context) {
    String contentType = request.getHeaders().get("content-type"); // Get content-type header
    // here the "content-type" must be lower-case
    String body = request.getBody().get(); // Get request body
    InputStream in = new ByteArrayInputStream(body.getBytes()); // Convert body to an input stream
    String boundary = contentType.split(";")[1].split("=")[1]; // Get boundary from content-type header
    int bufSize = 1024;
    MultipartStream multipartStream  = new MultipartStream(in, boundary.getBytes(), bufSize, null); // Using MultipartStream to parse body input stream
    // the code below comes from the SO thread above
    // you can fetch a file content from readBodyData 
    // after the headers Content-Disposition: form-data; name="upfile"; filename="test.txt" \n Content-Type: text/plain
    boolean nextPart = multipartStream.skipPreamble();
    while (nextPart) {
        String header = multipartStream.readHeaders();
        System.out.println("");
        System.out.println("Headers:");
        System.out.println(header);
        System.out.println("Body:");
        multipartStream.readBodyData(System.out);
        System.out.println("");
        nextPart = multipartStream.readBoundary();
    }
    return request.createResponseBuilder(HttpStatus.OK).body("Success").build();
}

Вывод кода выше в терминале:

Headers:
Content-Disposition: form-data; name="upfile"; filename="test.txt"
Content-Type: text/plain


Body:
1234
ABCD


Headers:
Content-Disposition: form-data; name="note"


Body:
test.txt

Обновление: при загрузке изображения вывод кода выше будет таким, как показано ниже.

Headers:
Content-Disposition: form-data; name="upfile"; filename="test.jpg"
Content-Type: image/png


Body:
<the binary content of an image>

Таким образом, вы можете проанализировать заголовок, чтобы получить значение filename и использовать FileOutputStream для его сохранения, как показано ниже.

while(nextPart) {
    String header = multipartStream.readHeaders();
    System.out.println("");
    System.out.println("Headers:");
    System.out.println(header);
    System.out.println("Body:");
    if (header.contains("Content-Type: image/")) {
        int start = header.indexOf("filename=")+"filename=".length()+1;
        int end = header.indexOf("\r\n")-1;
        String filename = header.substring(start, end);
        System.out.println(filename);
        FileOutputStream fos = new FileOutputStream(filename);
        multipartStream.readBodyData(fos);
    } else {
        multipartStream.readBodyData(System.out);
    }
    System.out.println("");
    nextPart = multipartStream.readBoundary();
}

Обновление 2:

Я обнаружил, что существует проблема Azure Function for Java, которая может быть ошибкой, которая приводит к потере некоторых байтов при загрузке двоичного файла, ноэто не произойдет при загрузке текстового файла.Поэтому обходным решением является преобразование файла загрузки в строку base64 в браузере для публикации в функцию Azure и преобразование содержимого base64, загруженного в двоичный файл источника в функции Azure.

Вот мой тестовый HTML-код.

File to upload: <input type="file" name="upfile" id="fileup"><br/>
<form method="POST" enctype="multipart/form-data" action="http://localhost:7071/api/HttpTrigger-Java">
  Notes about the file: <input type="text" name="note"><br/>
  <input type="hidden" name="file_base64" id="file_base64"><br/>
  <input type="submit" value="Press"> to upload the file!
</form>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">/script>
<script>
$(document).ready(function(){
    $("#fileup").change(function(){
        var v = $(this).val();
        var reader = new FileReader();
        reader.readAsDataURL(this.files[0]);
        reader.onload = function(e){
            console.log(e.target.result);
            $('#file_base64').val(e.target.result);
        };
    });
});
</script>

В приведенной выше форме будут размещены заголовок и тело фрагмента файла base64, как показано ниже.

Header:
Content-Disposition: form-data; name="file_base64"
Body:
data:image/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAB.............

Мой код Java в Azure Функция:

import java.io.ByteArrayOutputStream;
import java.util.Base64;

if (header.equals("Content-Disposition: form-data; name=\"file_base64\"")) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    multipartStream.readBodyData(baos);
    String content = baos.toString();
    // System.out.println(content);
    int index = content.indexOf(",")+1; // Get the index of base64 string in data-uploaded string
    byte[] imgBytes = Base64.getDecoder().decode(content.substring(index)); // convert image base64 string to image byte arrays
    ....
    // To upload image byte array to Blob Storage
    // You can get the upload image filename from the form input `note`, please notes the order of form input elements.
} else {
    multipartStream.readBodyData(System.out);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...