New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handling multipart requests #368
Changes from all commits
94c09ef
d378344
da7028d
89a5005
cb3e7e6
945605b
ea18a74
31bc8d3
730e846
8778515
4fd43cb
585f59c
ecd953b
c18d7a3
12d7c55
a2401b5
0fc2798
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,8 @@ public interface Context { | |
interface Impl extends Context { | ||
|
||
void setRoute(Route route); | ||
|
||
void cleanup(); | ||
} | ||
|
||
/** | ||
|
@@ -520,8 +522,37 @@ private HTTP_STATUS(int code) { | |
* @return the FileItemIterator of the request or null if there was an | ||
* error. | ||
*/ | ||
@Deprecated | ||
FileItemIterator getFileItemIterator(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we mark getFileItemIterator deprecated? |
||
|
||
/** | ||
* Gets the uploaded file item for the given key from the multipart request. | ||
* When multiple files uploaded for the same key, the first file is | ||
* returned. | ||
* | ||
* @param name the key of the file | ||
* @return file item instance corresponding to the uploaded file; | ||
* {@code null} if no file uploaded for the given key | ||
*/ | ||
NinjaFileItemStream getUploadedFileStream(String name); | ||
|
||
/** | ||
* Gets uploaded file items for the given key from the multipart request. | ||
* | ||
* @param name the key of files | ||
* @return list of file item instances corresponding to uploaded files; | ||
* empty list is returned if no files were uploaded for the given key | ||
*/ | ||
List<NinjaFileItemStream> getUploadedFileStreams(String name); | ||
|
||
/** | ||
* Gets file item instances corresponding to uploaded files from multipart | ||
* request. | ||
* | ||
* @return list of file items uploaded by a multipart request | ||
*/ | ||
List<NinjaFileItemStream> getFileItems(); | ||
|
||
/** | ||
* Get the validation context | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* | ||
* Copyright (C) 2012-2015 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 | ||
* | ||
* http://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 ninja; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.nio.file.Path; | ||
|
||
import org.apache.commons.fileupload.FileItemStream; | ||
|
||
/** | ||
* This interface represents uploaded file of a multipart request. Uploaded | ||
* files injected into controller methods are of this type. | ||
* | ||
*/ | ||
public interface NinjaFileItemStream { | ||
|
||
/** | ||
* Initializes this Ninja file item stream with given file item stream. | ||
* | ||
* @param fileItemStream file item stream to use values of | ||
*/ | ||
void init(FileItemStream fileItemStream); | ||
|
||
/** | ||
* Gets an {@link InputStream} that allows to read file contents. | ||
* | ||
* @return input stream from which file contents can be read | ||
* @throws IOException | ||
*/ | ||
InputStream openStream() throws IOException; | ||
|
||
/** | ||
* Copies contents of this uploaded file to specified target location. | ||
* | ||
* @param target file to copy contents to | ||
* @throws IOException | ||
*/ | ||
void copyTo(Path target) throws IOException; | ||
|
||
/** | ||
* Gets the name of the field in the multipart form corresponding to this | ||
* file item. | ||
* | ||
* @return The name of the form field. | ||
*/ | ||
String getFieldName(); | ||
|
||
/** | ||
* Returns the content type passed by the browser. | ||
* | ||
* @return The content type passed by the browser or {@code null} if not | ||
* defined. | ||
*/ | ||
String getContentType(); | ||
|
||
/** | ||
* Purges underlying resources of this file item. For file system backed | ||
* file items this method deletes temporary file where uploaded file | ||
* contents were saved. | ||
*/ | ||
void purge(); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ | |
import java.util.Map; | ||
|
||
import ninja.Context; | ||
import ninja.NinjaFileItemStream; | ||
import ninja.session.FlashScope; | ||
import ninja.session.Session; | ||
import ninja.validation.Validation; | ||
|
@@ -187,6 +188,59 @@ public String getFieldName() { | |
} | ||
} | ||
|
||
public static class FileExtractor implements ArgumentExtractor<NinjaFileItemStream> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you rename the class to NinjaFileItemStreamExtractor? |
||
|
||
private final String name; | ||
|
||
public FileExtractor(FileParam file) { | ||
this.name = file.value(); | ||
} | ||
|
||
@Override | ||
public NinjaFileItemStream extract(Context context) { | ||
return context.getUploadedFileStream(name); | ||
} | ||
|
||
@Override | ||
public Class<NinjaFileItemStream> getExtractedType() { | ||
return NinjaFileItemStream.class; | ||
} | ||
|
||
@Override | ||
public String getFieldName() { | ||
return name; | ||
} | ||
} | ||
|
||
public static class FilesExtractor implements ArgumentExtractor<NinjaFileItemStream[]> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you rename the class to NinjaFileItemStreamsExtractor? |
||
|
||
private final String name; | ||
|
||
public FilesExtractor(FileParams fileParams) { | ||
this.name = fileParams.value(); | ||
} | ||
|
||
@Override | ||
public NinjaFileItemStream[] extract(Context context) { | ||
List<NinjaFileItemStream> files = context.getUploadedFileStreams(name); | ||
if (files == null || files.isEmpty()) { | ||
return null; | ||
} | ||
return files.toArray(new NinjaFileItemStream[files.size()]); | ||
} | ||
|
||
@Override | ||
public Class<NinjaFileItemStream[]> getExtractedType() { | ||
return NinjaFileItemStream[].class; | ||
} | ||
|
||
@Override | ||
public String getFieldName() { | ||
return name; | ||
} | ||
|
||
} | ||
|
||
public static class HeaderExtractor implements ArgumentExtractor<String> { | ||
private final String key; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/** | ||
* Copyright (C) 2012-2015 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 | ||
* | ||
* http://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 ninja.params; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* Inject an uploaded file to a controller method. | ||
* | ||
* This equals context.getUploadedFile(...) | ||
* | ||
* @author azilet | ||
*/ | ||
@WithArgumentExtractor(ArgumentExtractors.FileExtractor.class) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Target({ElementType.PARAMETER}) | ||
public @interface FileParam { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you rename that to NinjaFileItemStreamParam? |
||
|
||
String value(); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/** | ||
* Copyright (C) 2012-2015 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 | ||
* | ||
* http://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 ninja.params; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* Inject multiple uploaded files with same key to a controller method. | ||
* | ||
* This equals context.getUploadedFiles(...) | ||
* | ||
* @author azilet | ||
*/ | ||
@WithArgumentExtractor(ArgumentExtractors.FilesExtractor.class) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Target({ElementType.PARAMETER}) | ||
public @interface FileParams { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename to NinjaFileItemStreamsParam? |
||
|
||
String value(); | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -143,7 +143,35 @@ public interface NinjaConstant { | |
* (XSS). | ||
*/ | ||
final String sessionHttpOnly = "application.session.http_only"; | ||
|
||
|
||
/** | ||
* Indicates if uploaded files should be handled fully in-memory without | ||
* saving to file system. Can be {@code true} or {@code false}, defaults to | ||
* {@code false}. | ||
*/ | ||
final String FILE_UPLOADS_IN_MEMORY = "file.uploads.in_memory"; | ||
|
||
/** | ||
* The maximum allowed size of a single uploaded file. | ||
* | ||
* @see org.apache.commons.fileupload.FileUploadBase#fileSizeMax | ||
*/ | ||
final String FILE_UPLOADS_MAX_FILE_SIZE = "file.uploads.file.size.max"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think we could simplify that and only use "FILE_UPLOADS_MAX_REQUEST_SIZE". It somewhat also includes the max files size and we got one property less :) |
||
|
||
/** | ||
* The maximum allowed size of a complete request, i.e. size of all uploaded | ||
* files. | ||
* | ||
* @see org.apache.commons.fileupload.FileUploadBase#sizeMax | ||
*/ | ||
final String FILE_UPLOADS_MAX_REQUEST_SIZE = "file.uploads.total.size.max"; | ||
|
||
/** | ||
* Directory where uploaded files are saved. Defaults to system's temporary | ||
* directory, i.e. "java.io.tmpdir" system property is consulted | ||
*/ | ||
final String FILE_UPLOADS_DIRECTORY = "file.uploads.directory"; | ||
|
||
// ///////////////////////////////////////////////// | ||
// Diagnostic mode - extension to dev mode where | ||
// ninja.Ninja is forced with ninja.diagnostics.NinjaDiagnostic | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -130,6 +130,9 @@ accepted by Joda Time library. | |
Therefore you don't have to worry if | ||
input is for instance Xml or Json. You simply get a parsed object. | ||
|
||
Additioanlly, uploaded file streams of a multipart request can be injected into your controller. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Additionally |
||
For more details on multipart requests please refer to | ||
<a href="/documentation/uploading_files.html">this page</a>. | ||
|
||
## Ninja and content negotiation | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we can rename that to cleanupAfterRequest() + add one line of javadoc...