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

org.springframework.http.codec.multipart.FilePartEvent 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.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.function.Consumer;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import org.springframework.core.io.Resource;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
 * Represents an event triggered for a file upload. Contains the
 * {@linkplain #filename() filename}, besides the {@linkplain #headers() headers}
 * and {@linkplain #content() content} exposed through {@link PartEvent}.
 *
 * 

On the client side, instances of this interface can be created via one * of the overloaded {@linkplain #create(String, Path) create} methods. * *

On the server side, multipart file uploads trigger one or more * {@code FilePartEvent}, as {@linkplain PartEvent described here}. * * @author Arjen Poutsma * @since 6.0 * @see PartEvent */ public interface FilePartEvent extends PartEvent { /** * Return the original filename in the client's filesystem. *

Note: Please keep in mind this filename is supplied * by the client and should not be used blindly. In addition to not using * the directory portion, the file name could also contain characters such * as ".." and others that can be used maliciously. It is recommended to not * use this filename directly. Preferably generate a unique one and save * this one somewhere for reference, if necessary. * @return the original filename, or the empty String if no file has been chosen * in the multipart form, or {@code null} if not defined or not available * @see RFC 7578, Section 4.2 * @see Unrestricted File Upload */ default String filename() { String filename = this.headers().getContentDisposition().getFilename(); Assert.state(filename != null, "No filename found"); return filename; } /** * Creates a stream of {@code FilePartEvent} objects based on the given * {@linkplain PartEvent#name() name} and resource. * @param name the name of the part * @param resource the resource * @return a stream of events */ static Flux create(String name, Resource resource) { return create(name, resource, null); } /** * Creates a stream of {@code FilePartEvent} objects based on the given * {@linkplain PartEvent#name() name} and resource. * @param name the name of the part * @param resource the resource * @param headersConsumer used to change default headers. Can be {@code null}. * @return a stream of events */ static Flux create(String name, Resource resource, @Nullable Consumer headersConsumer) { try { return create(name, resource.getFile().toPath(), headersConsumer); } catch (IOException ex) { return Flux.error(ex); } } /** * Creates a stream of {@code FilePartEvent} objects based on the given * {@linkplain PartEvent#name() name} and file path. * @param name the name of the part * @param path the file path * @return a stream of events */ static Flux create(String name, Path path) { return create(name, path, null); } /** * Creates a stream of {@code FilePartEvent} objects based on the given * {@linkplain PartEvent#name() name} and file path. * @param name the name of the part * @param path the file path * @param headersConsumer used to change default headers. Can be {@code null}. * @return a stream of events */ static Flux create(String name, Path path, @Nullable Consumer headersConsumer) { Assert.hasLength(name, "Name must not be empty"); Assert.notNull(path, "Path must not be null"); return Flux.defer(() -> { String pathName = StringUtils.cleanPath(path.toString()); MediaType contentType = MediaTypeFactory.getMediaType(pathName) .orElse(MediaType.APPLICATION_OCTET_STREAM); String filename = StringUtils.getFilename(pathName); if (filename == null) { return Flux.error(new IllegalArgumentException("Invalid file: " + pathName)); } Flux contents = DataBufferUtils.read(path, DefaultDataBufferFactory.sharedInstance, 8192); return create(name, filename, contentType, contents, headersConsumer); }); } /** * Creates a stream of {@code FilePartEvent} objects based on the given * {@linkplain PartEvent#name() name}, {@linkplain #filename()}, * content-type, and contents. * @param partName the name of the part * @param filename the filename * @param contentType the content-type for the contents * @param contents the contents * @return a stream of events */ static Flux create(String partName, String filename, MediaType contentType, Flux contents) { return create(partName, filename, contentType, contents, null); } /** * Creates a stream of {@code FilePartEvent} objects based on the given * {@linkplain PartEvent#name() name}, {@linkplain #filename()}, * content-type, and contents. * @param partName the name of the part * @param filename the filename * @param contentType the content-type for the contents * @param contents the contents * @param headersConsumer used to change default headers. Can be {@code null}. * @return a stream of events */ static Flux create(String partName, String filename, MediaType contentType, Flux contents, @Nullable Consumer headersConsumer) { Assert.hasLength(partName, "PartName must not be empty"); Assert.hasLength(filename, "Filename must not be empty"); Assert.notNull(contentType, "ContentType must not be null"); Assert.notNull(contents, "Contents must not be null"); return Flux.defer(() -> { HttpHeaders headers = new HttpHeaders(); headers.setContentType(contentType); headers.setContentDisposition(ContentDisposition.formData() .name(partName) .filename(filename, StandardCharsets.UTF_8) .build()); if (headersConsumer != null) { headersConsumer.accept(headers); } return contents.map(content -> DefaultPartEvents.file(headers, content, false)) .concatWith(Mono.just(DefaultPartEvents.file(headers))); }); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy