All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.http.codec.multipart.PartEvent Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.http.codec.multipart;

import java.nio.file.Path;
import java.util.function.BiFunction;
import java.util.function.Predicate;

import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;

import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.util.Assert;

/**
 * Represents an event for a "multipart/form-data" request.
 * Can be a {@link FormPartEvent} or a {@link FilePartEvent}.
 *
 * 

Server Side

* * Each part in a multipart HTTP message produces at least one * {@code PartEvent} containing both {@link #headers() headers} and a * {@linkplain PartEvent#content() buffer} with the contents of the part. *
    *
  • Form fields will produce a single {@link FormPartEvent}, * containing the {@linkplain FormPartEvent#value() value} of the field.
  • *
  • File uploads will produce one or more {@link FilePartEvent}s, * containing the {@linkplain FilePartEvent#filename() filename} used when * uploading. If the file is large enough to be split across multiple buffers, * the first {@code FilePartEvent} will be followed by subsequent events.
  • *
* The final {@code PartEvent} for a particular part will have * {@link #isLast()} set to {@code true}, and can be followed by * additional events belonging to subsequent parts. * The {@code isLast()} property is suitable as a predicate for the * {@link Flux#windowUntil(Predicate)} operator, in order to split events from * all parts into windows that each belong to a single part. * From that, the {@link Flux#switchOnFirst(BiFunction)} operator allows you to * see whether you are handling a form field or file upload. * For example: * *
 * Flux<PartEvent> allPartsEvents = ... // obtained via @RequestPayload or request.bodyToFlux(PartEvent.class)
 * allPartsEvents.windowUntil(PartEvent::isLast)
 *   .concatMap(p -> p.switchOnFirst((signal, partEvents) -> {
 *       if (signal.hasValue()) {
 *           PartEvent event = signal.get();
 *           if (event instanceof FormPartEvent formEvent) {
 *               String value = formEvent.value();
 *               // handle form field
 *           }
 *           else if (event instanceof FilePartEvent fileEvent) {
 *               String filename = fileEvent.filename();
 *               Flux<DataBuffer> contents = partEvents.map(PartEvent::content);
 *               // handle file upload
 *           }
 *           else {
 *               return Mono.error(new RuntimeException("Unexpected event: " + event));
 *           }
 *       }
 *       else {
 *         return partEvents; // either complete or error signal
 *       }
 *   }))
 * 
* Received part events can also be relayed to another service by using the * {@link org.springframework.web.reactive.function.client.WebClient WebClient}. * See below. * *

NOTE that the {@linkplain PartEvent#content() body contents} * must be completely consumed, relayed, or released to avoid memory leaks. * *

Client Side

* On the client side, {@code PartEvent}s can be created to represent a file upload. *
    *
  • Form fields can be created via {@link FormPartEvent#create(String, String)}.
  • *
  • File uploads can be created via {@link FilePartEvent#create(String, Path)}.
  • *
* The streams returned by these static methods can be concatenated via * {@link Flux#concat(Publisher[])} to create a request for the * {@link org.springframework.web.reactive.function.client.WebClient WebClient}: * For instance, this sample will POST a multipart form containing a form field * and a file. * *
 * Resource resource = ...
 * Mono<String> result = webClient
 *   .post()
 *   .uri("https://example.com")
 *   .body(Flux.concat(
 *     FormPartEvent.create("field", "field value"),
 *     FilePartEvent.create("file", resource)
 *   ), PartEvent.class)
 *   .retrieve()
 *   .bodyToMono(String.class);
 * 
* * @author Arjen Poutsma * @since 6.0 * @see FormPartEvent * @see FilePartEvent * @see PartEventHttpMessageReader * @see PartEventHttpMessageWriter */ public interface PartEvent { /** * Return the name of the event, as provided through the * {@code Content-Disposition name} parameter. * @return the name of the part, never {@code null} or empty */ default String name() { String name = headers().getContentDisposition().getName(); Assert.state(name != null, "No name available"); return name; } /** * Return the headers of the part that this event belongs to. */ HttpHeaders headers(); /** * Return the content of this event. The returned buffer must be consumed or * {@linkplain org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer) released}. */ DataBuffer content(); /** * Indicates whether this is the last event of a particular * part. */ boolean isLast(); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy