Загрузка нескольких файлов и объект json в одном запросе POST в Play Framework

У меня есть клиент Windows Phone 8, который делает следующий почтовый запрос:

public async Task<string> DoPostRequestAsync(String URI, JSonWriter jsonObject, ObservableCollection<byte[]> attachments)
    {
        var client = new RestClient(DefaultUri);

        var request = new RestRequest(URI, Method.POST);
        request.AddParameter("application/json; charset=utf-8", jsonObject.ToString(), ParameterType.RequestBody);

        // add files to upload
        foreach (var a in attachments)
            request.AddFile("picture", a, "file.jpg");

        var content = await client.GetResponseAsync(request);

        return content;
    }

Из документации RestSharp я прочитал, что при добавлении файлов к запросу он автоматически создается как запрос «multipart/form-data».

Контроллер для операции загрузки в Play 2.1 выглядит следующим образом:

@BodyParser.Of(BodyParser.Json.class)
public static Result createMessage() {
    JsonNode json = request().body().asJson();
    ObjectNode result = Json.newObject();
    String userId = json.findPath("userId").getTextValue();
    String rayz = json.findPath("message").getTextValue();

    Http.MultipartFormData body = request().body().asMultipartFormData();
    Http.MultipartFormData.FilePart picture = body.getFile("picture");

    if (picture != null) {
        String fileName = picture.getFilename();
        String contentType = picture.getContentType();
        File file = picture.getFile();

        result.put("status", "success");
        result.put("message", "Created message!");
        return badRequest(result);
    } else {
        result.put("status", "error");
        result.put("message", "Message cannot be created!");
        return badRequest(result);
    }
}

Обратите внимание, что в application.conf я установил следующее, чтобы увеличить ограничение размера (похоже, не работает):

# Application settings
# ~~~~~
parsers.text.maxLength=102400K

Теперь каждый раз, когда я пытаюсь выполнить запрос POST, я замечаю в отладчике, что переменная IsMaxSizeEsceeded всегда имеет значение true, а составная переменная имеет значение null. Когда я попытался просто загрузить один файл nu, используя следующий контроллер, все, казалось, работало нормально. Размер не был проблемой, и переменная multipart была установлена.

public static Result singleUpload() {
    ObjectNode result = Json.newObject();

    Http.MultipartFormData body = request().body().asMultipartFormData();
    Http.MultipartFormData.FilePart picture = body.getFile("picture");
    if (picture != null) {
        String fileName = picture.getFilename();
        String contentType = picture.getContentType();
        File file = picture.getFile();
        result.put("status", "success");
        result.put("message", "File uploaded!");
        return badRequest(result);
    } else {
        result.put("status", "error");
        result.put("message", "File cannot be uploaded!");
        return badRequest(result);
    }
}

Проблема в том, что вложения/файлы должны быть отправлены/загружены вместе с объектом JSON на сервер в одном запросе POST, а НЕ отдельно.

Кто-нибудь сталкивался с подобными проблемами раньше? Возможно ли добиться этого — отправить объект json и несколько файлов для загрузки на сервер в одном запросе POST с помощью Play 2.1?


person George Nikolaides    schedule 10.07.2013    source источник


Ответы (1)


Нашел способ сделать это ... опубликовать его на случай, если кто-то еще попытается сделать что-то подобное в будущем.

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

public async Task<string> DoPostRequestWithAttachmentsAsync(String ext, JSonWriter jsonObject, ObservableCollection<byte[]> attachments) {
    var client = new RestClient(DefaultUri);
    var request = new RestRequest(ext, Method.POST);

    request.RequestFormat = DataFormat.Json;
    request.AddParameter("json", jsonObject.ToString(), ParameterType.GetOrPost);

    // add files to upload
    foreach (var a in attachments)
        request.AddFile("attachment", a, "someFileName");

    var content = await client.GetResponseAsync(request);

    if (content.StatusCode != HttpStatusCode.OK)
            return <error>;

    return content.Content;
}

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

public static Result createMessage() {
    List<Http.MultipartFormData.FilePart> attachments;
    String json_str;

    Http.MultipartFormData body = request().body().asMultipartFormData();

    // If the body is multipart get the json object asMultipartFormData()
    if (body!=null) {
        json_str = request().body().asMultipartFormData().asFormUrlEncoded().get("json")[0];
        attachments= body.getFiles();
    }
    // Else, if the body is not multipart get the json object asFormUrlEncoded()
    else {
        json_str = request().body().asFormUrlEncoded().get("json")[0];
        attachments = Collections.emptyList();
    }

    // Parse the Json Object
    JsonNode json = Json.parse(json_str);

    // Iterate through the uploaded files and save them to the server
    for (Http.MultipartFormData.FilePart o : attachments)
        FileManager.SaveAttachmentToServer(o.getContentType(), o.getFile());

    return ok();
}

Таким образом, ошибки на стороне RestSharp, похоже, были ParameterType.RequestBody в объекте json; и в контроллере размер на самом деле ничего не меняет ... но важная часть заключается в том, чтобы не использовать @BodyParser.Of(BodyParser.Json.class), поскольку это преобразовало бы все тело запроса в объект json. Это в сочетании с файлами, отправляемыми на сервер, вызывает флаг isMaxSizeExceeded.

Наконец, обработка составных файлов в контроллере показана выше, только сложная часть заключается в том, что вложения являются необязательными, что означает, что это должно быть обработано.

person George Nikolaides    schedule 12.07.2013