@Test public void getMultipartParts_works_as_expected_with_known_valid_data() throws IOException { // given RequestInfoImpl<?> requestInfo = RequestInfoImpl.dummyInstanceForUnknownRequests(); Whitebox.setInternalState(requestInfo, "isMultipart", true); Whitebox.setInternalState(requestInfo, "contentCharset", CharsetUtil.UTF_8); Whitebox.setInternalState(requestInfo, "protocolVersion", HttpVersion.HTTP_1_1); Whitebox.setInternalState(requestInfo, "method", HttpMethod.POST); requestInfo.isCompleteRequestWithAllChunks = true; requestInfo.rawContentBytes = KNOWN_MULTIPART_DATA_BODY.getBytes(CharsetUtil.UTF_8); requestInfo.getHeaders().set("Content-Type", KNOWN_MULTIPART_DATA_CONTENT_TYPE_HEADER); // when List<InterfaceHttpData> result = requestInfo.getMultipartParts(); // then assertThat(result, notNullValue()); assertThat(result.size(), is(1)); InterfaceHttpData data = result.get(0); assertThat(data, instanceOf(FileUpload.class)); FileUpload fileUploadData = (FileUpload)data; assertThat(fileUploadData.getName(), is(KNOWN_MULTIPART_DATA_NAME)); assertThat(fileUploadData.getFilename(), is(KNOWN_MULTIPART_DATA_FILENAME)); assertThat(fileUploadData.getString(CharsetUtil.UTF_8), is(KNOWN_MULTIPART_DATA_ATTR_UUID)); }
private void writeHttpData(InterfaceHttpData data) throws IOException { if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) { Attribute attribute = (Attribute) data; String value = attribute.getValue(); if (value.length() > 65535) { throw new IOException("Data too long"); } req.addPostParameter(attribute.getName(), value); } else { if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) { FileUpload fileUpload = (FileUpload) data; req.addFileUpload(fileUpload); } } }
private String toString(InterfaceHttpData p) { return callUnchecked(() -> { if (p != null) { if (p instanceof FileUpload) { return FileUpload.class.cast(p).getFilename(); } if (p instanceof Attribute) { return Attribute.class.cast(p).getValue(); } else { return null; } } else { return null; } }); }
public FileUploadInputStream(FileUpload fileUpload, int bufSize) { super(new InputStream() { @Override public int read() throws IOException { throw new IllegalStateException("Implementation error!"); } public int read(byte [] buf, int pos, int len) throws IOException { ByteBuf buffer = fileUpload.getChunk(len); if (buffer.readableBytes() == 0) { return -1; } else { int cc = len > buffer.readableBytes() ? buffer.readableBytes() : len; buffer.readBytes(buf, pos, cc); return cc; } } }, bufSize); }
/** * Creates a {@link HttpPostRequestEncoder} that encodes the given {@code request} and {@code blobContent}. * @param request the {@link HttpRequest} containing headers and other metadata about the request. * @param blobContent the {@link ByteBuffer} that represents the content of the blob. * @param usermetadata the {@link ByteBuffer} that represents user metadata * @return a {@link HttpPostRequestEncoder} that can encode the {@code request} and {@code blobContent}. * @throws HttpPostRequestEncoder.ErrorDataEncoderException * @throws IOException */ private HttpPostRequestEncoder createEncoder(HttpRequest request, ByteBuffer blobContent, ByteBuffer usermetadata) throws HttpPostRequestEncoder.ErrorDataEncoderException, IOException { HttpDataFactory httpDataFactory = new DefaultHttpDataFactory(false); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(httpDataFactory, request, true); FileUpload fileUpload = new MemoryFileUpload(RestUtils.MultipartPost.BLOB_PART, RestUtils.MultipartPost.BLOB_PART, "application/octet-stream", "", Charset.forName("UTF-8"), blobContent.remaining()); fileUpload.setContent(Unpooled.wrappedBuffer(blobContent)); encoder.addBodyHttpData(fileUpload); fileUpload = new MemoryFileUpload(RestUtils.MultipartPost.USER_METADATA_PART, RestUtils.MultipartPost.USER_METADATA_PART, "application/octet-stream", "", Charset.forName("UTF-8"), usermetadata.remaining()); fileUpload.setContent(Unpooled.wrappedBuffer(usermetadata)); encoder.addBodyHttpData(fileUpload); return encoder; }
/** * Creates a {@link HttpPostRequestEncoder} that encodes the given {@code request} and {@code parts}. * @param request the {@link HttpRequest} containing headers and other metadata about the request. * @param parts the {@link InMemoryFile}s that will form the parts of the request. * @return a {@link HttpPostRequestEncoder} that can encode the {@code request} and {@code parts}. * @throws HttpPostRequestEncoder.ErrorDataEncoderException * @throws IOException */ private HttpPostRequestEncoder createEncoder(HttpRequest request, InMemoryFile[] parts) throws HttpPostRequestEncoder.ErrorDataEncoderException, IOException { HttpDataFactory httpDataFactory = new DefaultHttpDataFactory(false); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(httpDataFactory, request, true); if (parts != null) { for (InMemoryFile part : parts) { FileUpload fileUpload = new MemoryFileUpload(part.name, part.name, "application/octet-stream", "", Charset.forName("UTF-8"), part.content.remaining()); fileUpload.setContent(Unpooled.wrappedBuffer(part.content)); encoder.addBodyHttpData(fileUpload); } } return encoder; }
@Override public CompletableFuture<ResponseInfo<String>> execute(RequestInfo<String> request, Executor longRunningTaskExecutor, ChannelHandlerContext ctx) { List<String> hashesFound = new ArrayList<>(); for (InterfaceHttpData multipartData : request.getMultipartParts()) { String name = multipartData.getName(); byte[] payloadBytes; try { payloadBytes = ((HttpData)multipartData).get(); } catch (IOException e) { throw new RuntimeException(e); } String filename = null; switch (multipartData.getHttpDataType()) { case Attribute: // Do nothing - filename stays null break; case FileUpload: filename = ((FileUpload)multipartData).getFilename(); break; default: throw new RuntimeException("Unsupported multipart type: " + multipartData.getHttpDataType().name()); } hashesFound.add(getHashForMultipartPayload(name, filename, payloadBytes)); } return CompletableFuture.completedFuture(ResponseInfo.newBuilder(StringUtils.join(hashesFound, ",")).build()); }
private HttpResponseStatus readFileUploadData() throws IOException { while (decoder.hasNext()) { final InterfaceHttpData data = decoder.next(); if (data != null) { try { logger.info("BODY FileUpload: " + data.getHttpDataType().name() + ": " + data); if (data.getHttpDataType() == HttpDataType.FileUpload) { final FileUpload fileUpload = (FileUpload) data; if (fileUpload.isCompleted()) { requestProcessed = true; final String format = ImageStoreUtil.checkTemplateFormat(fileUpload.getFile().getAbsolutePath(), fileUpload.getFilename()); if (StringUtils.isNotBlank(format)) { final String errorString = "File type mismatch between the sent file and the actual content. Received: " + format; logger.error(errorString); responseContent.append(errorString); storageResource.updateStateMapWithError(uuid, errorString); return HttpResponseStatus.BAD_REQUEST; } final String status = storageResource.postUpload(uuid, fileUpload.getFile().getName()); if (status != null) { responseContent.append(status); storageResource.updateStateMapWithError(uuid, status); return HttpResponseStatus.INTERNAL_SERVER_ERROR; } else { responseContent.append("upload successful."); return HttpResponseStatus.OK; } } } } finally { data.release(); } } } responseContent.append("received entity is not a file"); return HttpResponseStatus.UNPROCESSABLE_ENTITY; }
private void handleUploadFile(InterfaceHttpData data, Message uploadMessage) throws IOException{ FileForm fileForm = uploadMessage.fileForm; if(uploadMessage.fileForm == null){ uploadMessage.fileForm = fileForm = new FileForm(); } if (data.getHttpDataType() == HttpDataType.Attribute) { Attribute attribute = (Attribute) data; fileForm.attributes.put(attribute.getName(), attribute.getValue()); return; } if (data.getHttpDataType() == HttpDataType.FileUpload) { FileUpload fileUpload = (FileUpload) data; Message.FileUpload file = new Message.FileUpload(); file.fileName = fileUpload.getFilename(); file.contentType = fileUpload.getContentType(); file.data = fileUpload.get(); List<Message.FileUpload> uploads = fileForm.files.get(data.getName()); if(uploads == null){ uploads = new ArrayList<Message.FileUpload>(); fileForm.files.put(data.getName(), uploads); } uploads.add(file); } }
@RequestMapping(value = "/test3", supportMultipart = true) public Object test3(@RequestParam(value = "name") String[] name, @RequestParam("age") Integer[] age, @RequestParam("isgood") boolean[] isgood, @RequestParam("file") FileUpload fu) throws IOException { System.out.println(Arrays.toString(name) + ", " + Arrays.toString(age) + ", " + Arrays.toString(isgood)); try { System.out.println(fu.getName() + ", " + fu.getFilename() + ", " + fu.getContentType() + ": " + fu.getString()); } catch (Exception e) { e.printStackTrace(); } return "OK"; }
public MultiPart getMultiPart(String name) { return ofNullable(decoder.getBodyHttpData(name)) .filter(p -> p instanceof FileUpload) .map(p -> new MultiPart((FileUpload) p)) .orElse(null); }
/** * Add a file as a FileUpload * * @param name * the name of the parameter * @param file * the file to be uploaded (if not Multipart mode, only the filename will be included) * @param contentType * the associated contentType for the File * @param isText * True if this file should be transmitted in Text format (else binary) * @throws NullPointerException * for name and file * @throws ErrorDataEncoderException * if the encoding is in error or if the finalize were already done */ public void addBodyFileUpload(String name, File file, String contentType, boolean isText) throws ErrorDataEncoderException { if (name == null) { throw new NullPointerException("name"); } if (file == null) { throw new NullPointerException("file"); } String scontentType = contentType; String contentTransferEncoding = null; if (contentType == null) { if (isText) { scontentType = HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE; } else { scontentType = HttpPostBodyUtil.DEFAULT_BINARY_CONTENT_TYPE; } } if (!isText) { contentTransferEncoding = HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value(); } FileUpload fileUpload = factory.createFileUpload(request, name, file.getName(), scontentType, contentTransferEncoding, null, file.length()); try { fileUpload.setContent(file); } catch (IOException e) { throw new ErrorDataEncoderException(e); } addBodyHttpData(fileUpload); }
/** * 添加流的上传 * @param name * @param inputStream * @param contentType * @param isText * 添加(修改)人:zhuyuping * @throws ErrorDataEncoderException */ public void addBodyStreamUpload(String name,FileChunk fileChunk, String contentType, boolean isText) { if (name == null) { throw new NullPointerException("name"); } if (fileChunk == null) { throw new NullPointerException("fileChunk"); } String scontentType = contentType; String contentTransferEncoding = null; if (contentType == null) { if (isText) { scontentType = HttpPostBodyUtil.DEFAULT_TEXT_CONTENT_TYPE; } else { scontentType = HttpPostBodyUtil.DEFAULT_BINARY_CONTENT_TYPE; } } if (!isText) { contentTransferEncoding = HttpPostBodyUtil.TransferEncodingMechanism.BINARY.value(); } byte[] chunks=fileChunk.getBytes(); FileUpload fileUpload = factory.createFileUpload(request, name, String.valueOf(fileChunk.getChunk()), scontentType, contentTransferEncoding, null, chunks.length); factory.createAttribute(request, "chunks",String.valueOf(fileChunk.getChunk())); factory.createAttribute(request, "checksum",fileChunk.getHash()); try { fileUpload.setContent(wrappedBuffer(fileChunk.getBytes())); addBodyHttpData(fileUpload); } catch (Exception e) { throw new RuntimeException(e); } }
private void addToParameters(Map<String, List<String>> parameters, FileUpload fileUpload) { try { File uploadedFile = fileUpload.getFile(); parameters.put(fileUpload.getName() + FILE, Lists.newArrayList(uploadedFile.getAbsolutePath())); parameters.put(fileUpload.getName() + FILENAME, Lists.newArrayList(fileUpload.getFilename())); parameters.put(fileUpload.getName() + CONTENT_TYPE, Lists.newArrayList(fileUpload.getContentType())); } catch (Exception e) { throw new FileUploadException("Failed to upload file: " + e.getMessage(), e); } }
private HttpResponseStatus readFileUploadData() throws IOException { while (decoder.hasNext()) { InterfaceHttpData data = decoder.next(); if (data != null) { try { logger.info("BODY FileUpload: " + data.getHttpDataType().name() + ": " + data); if (data.getHttpDataType() == HttpDataType.FileUpload) { FileUpload fileUpload = (FileUpload) data; if (fileUpload.isCompleted()) { requestProcessed = true; String format = ImageStoreUtil.checkTemplateFormat(fileUpload.getFile().getAbsolutePath(), fileUpload.getFilename()); if(StringUtils.isNotBlank(format)) { String errorString = "File type mismatch between the sent file and the actual content. Received: " + format; logger.error(errorString); responseContent.append(errorString); storageResource.updateStateMapWithError(uuid, errorString); return HttpResponseStatus.BAD_REQUEST; } String status = storageResource.postUpload(uuid, fileUpload.getFile().getName()); if (status != null) { responseContent.append(status); storageResource.updateStateMapWithError(uuid, status); return HttpResponseStatus.INTERNAL_SERVER_ERROR; } else { responseContent.append("upload successful."); return HttpResponseStatus.OK; } } } } finally { data.release(); } } } responseContent.append("received entity is not a file"); return HttpResponseStatus.UNPROCESSABLE_ENTITY; }
/** * Creates a {@link HttpPostRequestEncoder} that encodes the given {@code request} and {@code blobContent}. * @param request the {@link HttpRequest} containing headers and other metadata about the request. * @param blobContent the {@link ByteBuffer} that represents the content of the blob. * @return a {@link HttpPostRequestEncoder} that can encode the {@code request} and {@code blobContent}. * @throws HttpPostRequestEncoder.ErrorDataEncoderException * @throws IOException */ private HttpPostRequestEncoder createEncoder(HttpRequest request, ByteBuffer blobContent) throws HttpPostRequestEncoder.ErrorDataEncoderException, IOException { HttpDataFactory httpDataFactory = new DefaultHttpDataFactory(false); HttpPostRequestEncoder encoder = new HttpPostRequestEncoder(httpDataFactory, request, true); FileUpload fileUpload = new MemoryFileUpload(RestUtils.MultipartPost.BLOB_PART, RestUtils.MultipartPost.BLOB_PART, "application/octet-stream", "", Charset.forName("UTF-8"), blobContent.remaining()); fileUpload.setContent(Unpooled.wrappedBuffer(blobContent)); encoder.addBodyHttpData(fileUpload); return encoder; }
public List<FileUpload> getUploadedFiles() { return uploadedFiles; }
public void setUploadedFiles(List<FileUpload> uploadedFiles) { this.uploadedFiles = uploadedFiles; }
protected UploadedFileImpl(FileUpload fileUpload) { this.fileUpload = fileUpload; }
protected void addFileUpload(FileUpload fileUpload) { fileUpload.retain(); this.uploadedFiles.add(fileUpload); }
public MultiPart(FileUpload fileUpload) { this.fileUpload = fileUpload; }
public NettyFile(FileUpload upload) { this.upload = upload; }
public NettyUpload(final FileUpload data, final String tmpdir) throws IOException { this.data = data; String name = "tmp-" + Long.toHexString(System.currentTimeMillis()) + "." + name(); file = new File(tmpdir, name); data.renameTo(file); }
@Override public HttpDataType getHttpDataType() { return HttpDataType.FileUpload; }
@Override public FileUpload retain(int increment) { super.retain(increment); return this; }
@Override public FileUpload retain() { super.retain(); return this; }
@Override public FileUpload copy() { return null; }
@Override public FileUpload duplicate() { return null; }
public FileUploadInputStream(FileUpload fileUpload) { this(fileUpload, DEFAULT_BUFFER_SIZE); }
/** * Processes a single decoded part in a multipart request. Exposes the data in the part either through the channel * itself (if it is the blob part) or via {@link #getArgs()}. * @param part the {@link InterfaceHttpData} that needs to be processed. * @throws RestServiceException if the request channel is closed, if there is more than one part of the same name, if * the size obtained from the headers does not match the actual size of the blob part or * if {@code part} is not of the expected type ({@link FileUpload}). */ private void processPart(InterfaceHttpData part) throws RestServiceException { if (part.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) { FileUpload fileUpload = (FileUpload) part; if (fileUpload.getName().equals(RestUtils.MultipartPost.BLOB_PART)) { // this is actual data. if (hasBlob) { nettyMetrics.repeatedPartsError.inc(); throw new RestServiceException("Request has more than one " + RestUtils.MultipartPost.BLOB_PART, RestServiceErrorCode.BadRequest); } else { hasBlob = true; if (getSize() != -1 && fileUpload.length() != getSize()) { nettyMetrics.multipartRequestSizeMismatchError.inc(); throw new RestServiceException( "Request size [" + fileUpload.length() + "] does not match Content-Length [" + getSize() + "]", RestServiceErrorCode.BadRequest); } else { contentLock.lock(); try { if (isOpen()) { requestContents.add(new DefaultHttpContent(ReferenceCountUtil.retain(fileUpload.content()))); } else { nettyMetrics.multipartRequestAlreadyClosedError.inc(); throw new RestServiceException("Request is closed", RestServiceErrorCode.RequestChannelClosed); } } finally { contentLock.unlock(); } } } } else { // this is any kind of data. (For ambry, this will be user metadata). // TODO: find a configurable way of rejecting unexpected file parts. String name = fileUpload.getName(); if (allArgs.containsKey(name)) { nettyMetrics.repeatedPartsError.inc(); throw new RestServiceException("Request already has a component named " + name, RestServiceErrorCode.BadRequest); } else { ByteBuffer buffer = ByteBuffer.allocate(fileUpload.content().readableBytes()); // TODO: Possible optimization - Upgrade ByteBufferReadableStreamChannel to take a list of ByteBuffer. This // TODO: will avoid the copy. fileUpload.content().readBytes(buffer); buffer.flip(); allArgs.put(name, buffer); } } } else { nettyMetrics.unsupportedPartError.inc(); throw new RestServiceException("Unexpected HTTP data", RestServiceErrorCode.BadRequest); } }
List<FileUpload> getUploadedFiles();