/// <summary> /// MultiformUploadRequest /// </summary> /// <param name="input"></param> /// <param name="boundary"></param> public MultiformUploadRequest(Stream input, string boundary) { parser = new StreamingMultipartFormDataParser(input, boundary, Encoding.UTF8, 1024 * 1024); parser.FileHandler = new StreamingMultipartFormDataParser.FileStreamDelegate(FileStreamHandler); parser.ParameterHandler = new StreamingMultipartFormDataParser.ParameterDelegate(ParameterHandler); parser.StreamClosedHandler = new StreamingMultipartFormDataParser.StreamClosedDelegate(StreamClosedHandler); parser.Run(); }
public void ParseAsMultipartStream(Action <string, string, string, string, byte[], int> onData, Action <string, string> onParameter = null) { var parser = new StreamingMultipartFormDataParser(this.InputStream); if (onParameter != null) { parser.ParameterHandler += parameter => onParameter(parameter.Name, parameter.Data); } parser.FileHandler += (name, fileName, type, disposition, buffer, bytes) => onData(name, fileName, type, disposition, buffer, bytes); parser.Run(); }
/// <summary> /// 处理多段表单数据 /// </summary> /// <param name="req"></param> /// <param name="parameterHandler"></param> /// <param name="fileHandler"></param> public static void HandleMultipartData(IOwinRequest req, StreamingMultipartFormDataParser.ParameterDelegate parameterHandler, StreamingMultipartFormDataParser.FileStreamDelegate fileHandler, StreamingMultipartFormDataParser.StreamClosedDelegate streamClosedDelegate = null) { if (!req.ContentType.StartsWith("multipart/form-data;")) { throw new ArgumentException("'ContentType' not start with 'multipart/form-data;'."); } var parser = new StreamingMultipartFormDataParser(req.Body); parser.ParameterHandler = parameterHandler; parser.FileHandler = fileHandler; parser.StreamClosedHandler = streamClosedDelegate; parser.Run(); }
/// <summary> /// Save multipart form-data from request body, if any, to a file in the specified directory /// </summary> /// <param name="filePath">The directory to placed to file in</param> /// <param name="filerenamer">Function to rename the file(s)</param> /// <param name="maxSizeKb">The max filesize allowed</param> /// <returns>Whether the file was saved succesfully</returns> public Task <bool> SaveBodyToFile(string filePath, Func <string, string> filerenamer = null, long maxSizeKb = 1024) { if (!UnderlyingRequest.HasEntityBody) { return(Task.FromResult(false)); } var maxbytes = maxSizeKb << 10; var tcs = new TaskCompletionSource <bool>(); var filestreams = new Dictionary <string, Stream>(); var files = new Dictionary <string, string>(); var sizes = new Dictionary <string, long>(); var failed = false; var parser = new StreamingMultipartFormDataParser(UnderlyingRequest.InputStream); parser.FileHandler += (name, fname, type, disposition, buffer, bytes) => { if (failed) { return; } if (bytes > maxbytes) { tcs.TrySetResult(false); return; } if (filerenamer != null) { string rename; if (!files.TryGetValue(fname, out rename)) { rename = filerenamer(fname); files.Add(fname, rename); } fname = rename; } if (!sizes.ContainsKey(fname)) { sizes[fname] = bytes; } else { sizes[fname] += bytes; } if (sizes[fname] > maxbytes) { failed = true; foreach (var str in filestreams.Values) { str.Close(); } tcs.TrySetResult(false); return; } Stream stream; if (!filestreams.TryGetValue(fname, out stream)) { stream = File.Create(Path.Combine(filePath, fname)); filestreams.Add(fname, stream); } stream.Write(buffer, 0, bytes); stream.Flush(); }; parser.StreamClosedHandler += () => { foreach (var stream in filestreams.Values) { stream.Close(); } tcs.TrySetResult(true); }; parser.Run(); return(tcs.Task); }
/// <summary> /// Processes the request to get the form values. /// </summary> /// <param name="request">The request.</param> /// <param name="bodyStream">The stream for the request's body.</param> /// <returns>The collection with all values from the form. The collection is possibly empty.</returns> public static NameValueCollection GetForm(this HttpListenerRequest request, Stream bodyStream) { if (request == null || bodyStream == null) { return(new NameValueCollection()); } if (!request.HasEntityBody) { return(new NameValueCollection()); } // Case #1: Just a form // Content Type = application/x-www-form-urlencoded // Body = MAX_FILE_SIZE=100000&t%C3%A4stValue=das+ist+ein+test if (request.ContentType.Contains("application/x-www-form-urlencoded")) { using (bodyStream) { using (var reader = new StreamReader(bodyStream)) { return(reader.ReadToEnd().GetQueryString(true)); } } } // Case #2: Just a file // Content type = multipart/form-data; boundary=----WebKitFormBoundaryeghC9TqseFEkpCXg // Body = // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="meinFileö"; filename="Short Time Notes.txt" // Content-Type: text/plain // // DATA STREAM // ------WebKitFormBoundaryeghC9TqseFEkpCXg-- // // ==> Does not matter // // Case #3: Multiple files // Content type = multipart/form-data; boundary=----WebKitFormBoundaryeghC9TqseFEkpCXg // Body = // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="meinFileö"; filename="Short Time Notes.txt" // Content-Type: text/plain // // DATA STREAM // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="meinFile2"; filename="fsdfsdf.txt" // Content-Type: text/plain // // DATA STREAM // ------WebKitFormBoundaryeghC9TqseFEkpCXg-- // // ==> Does not matter // // Case #4: A file and form data // Content type = multipart/form-data; boundary=----WebKitFormBoundaryeghC9TqseFEkpCXg // Body = // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="meinFileö"; filename="Short Time Notes.txt" // Content-Type: text/plain // // DATA STREAM // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="einsÖÄÜ" // // 2354öäpü // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="zwei" // // [email protected] // ------WebKitFormBoundaryeghC9TqseFEkpCXg-- // // ==> Does not matter, use case #5. // // Case #5: Multiple files and form data // Content type = multipart/form-data; boundary=----WebKitFormBoundaryeghC9TqseFEkpCXg // Body = // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="meinFileö"; filename="Short Time Notes.txt" // Content-Type: text/plain // // DATA STREAM // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="einsÖÄÜ" // // 2354öäpü // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="zwei" // // [email protected] // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="meinFile3"; filename="Ssdfsdfd.fdf" // Content-Type: text/plain // // DATA STREAM // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="rterz" // // [email protected] // ------WebKitFormBoundaryeghC9TqseFEkpCXg-- // Check the requirements: if (!request.ContentType.Contains("multipart/form-data; boundary=")) { return(new NameValueCollection()); } // Store the result here: var result = new NameValueCollection(); using (bodyStream) { // Dummy to consume the name of each parameter: var dummy = string.Empty; // Create the parser for the body: var smftp = new StreamingMultipartFormDataParser(bodyStream); // The handler for parameters: smftp.ParameterHandler += parameter => { result.Add(parameter.Name, parameter.Data); }; // The parser requires both, the parameter and the file handler: smftp.FileHandler += (name, fileName, type, disposition, buffer, bytes) => { // Perform a dummy OP: dummy = name; }; // Start the processing. This call will block until end of processing: smftp.Run(); } return(result); }
/// <summary> /// Processes the request to get all uploaded files. /// </summary> /// <param name="request">The request.</param> /// <param name="bodyStream">The stream for the request's body.</param> /// <param name="tmpFolderPATH">The temporary folder for this request.</param> /// <param name="niiRequest">The NoIIS request to register additional open streams.</param> /// <returns>Returns the collection with all uploaded files. The collection is possibily empty.</returns> public static HttpFileCollectionBase GetFiles(this HttpListenerRequest request, Stream bodyStream, string tmpFolderPATH, NoIISRequest niiRequest) { if (request == null || tmpFolderPATH == null || bodyStream == null) { return(new NoIISFileCollection()); } if (!request.HasEntityBody) { return(new NoIISFileCollection()); } // Case #1: Just a form // Content Type = application/x-www-form-urlencoded // Body = MAX_FILE_SIZE=100000&t%C3%A4stValue=das+ist+ein+test // // ==> Does not matter // // Case #2: Just a file // Content type = multipart/form-data; boundary=----WebKitFormBoundaryeghC9TqseFEkpCXg // Body = // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="meinFileö"; filename="Short Time Notes.txt" // Content-Type: text/plain // // DATA STREAM // ------WebKitFormBoundaryeghC9TqseFEkpCXg-- // // ==> Does not matter // // Case #3: Multiple files // Content type = multipart/form-data; boundary=----WebKitFormBoundaryeghC9TqseFEkpCXg // Body = // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="meinFileö"; filename="Short Time Notes.txt" // Content-Type: text/plain // // DATA STREAM // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="meinFile2"; filename="fsdfsdf.txt" // Content-Type: text/plain // // DATA STREAM // ------WebKitFormBoundaryeghC9TqseFEkpCXg-- // // ==> Does not matter // // Case #4: A file and form data // Content type = multipart/form-data; boundary=----WebKitFormBoundaryeghC9TqseFEkpCXg // Body = // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="meinFileö"; filename="Short Time Notes.txt" // Content-Type: text/plain // // DATA STREAM // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="einsÖÄÜ" // // 2354öäpü // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="zwei" // // [email protected] // ------WebKitFormBoundaryeghC9TqseFEkpCXg-- // // ==> Does not matter, use case #5. // // Case #5: Multiple files and form data // Content type = multipart/form-data; boundary=----WebKitFormBoundaryeghC9TqseFEkpCXg // Body = // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="meinFileö"; filename="Short Time Notes.txt" // Content-Type: text/plain // // DATA STREAM // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="einsÖÄÜ" // // 2354öäpü // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="zwei" // // [email protected] // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="meinFile3"; filename="Ssdfsdfd.fdf" // Content-Type: text/plain // // DATA STREAM // ------WebKitFormBoundaryeghC9TqseFEkpCXg // Content-Disposition: form-data; name="rterz" // // [email protected] // ------WebKitFormBoundaryeghC9TqseFEkpCXg-- // If the request's content type does not match the requirements, // return here: if (!request.ContentType.Contains("multipart/form-data; boundary=")) { return(new NoIISFileCollection()); } // The cache for all tmp. filenames for files in the request: var cacheFilenames = new ConcurrentDictionary <string, NoIISPostedFile>(); using (bodyStream) { // Dummy to consume the name of each parameter: var dummy = string.Empty; // Create the parser for the body: var smftp = new StreamingMultipartFormDataParser(bodyStream); // The parser requires both, the parameter and the file handler: smftp.ParameterHandler += parameter => dummy = parameter.Name; // The file handler gets called for every chunk of each file inside: smftp.FileHandler += (name, fileName, type, disposition, buffer, bytes) => { // Generate a key for this file: var key = name + fileName + type + disposition; // The destination tmp. file name: var destination = string.Empty; if (!cacheFilenames.ContainsKey(key)) { // Case: A new file. cacheFilenames[key] = new NoIISPostedFile(); // Generate the tmp. file name with path: cacheFilenames[key].TMPFilenamePATH = tmpFolderPATH + Guid.NewGuid().ToString(); cacheFilenames[key].FormName = name; cacheFilenames[key].setContentType(type); cacheFilenames[key].setFileName(fileName); } // Read the current destination: destination = cacheFilenames[key].TMPFilenamePATH; try { // Append the bytes to the tmp. file: using (var fileStream = File.Open(destination, FileMode.Append, FileAccess.Write, FileShare.None)) { fileStream.Write(buffer, 0, bytes); } } catch (Exception e) { Console.WriteLine("Exception while processing the file upload '{1}' (name='{2}'): {0}", e.Message, fileName, name); Console.WriteLine(e.StackTrace); } }; try { // Start the processing. This call will block until end of processing: smftp.Run(); } catch (Exception e) { Console.WriteLine("Exception while processing the file upload: {0}", e.Message); Console.WriteLine(e.StackTrace); } } // // The files should now be available as tmp. data. // // The result's collection: var result = new List <NoIISPostedFile>(cacheFilenames.Count); // Loop over all files: foreach (var key in cacheFilenames.Keys) { // Get a file: var file = cacheFilenames[key]; // Get the tmp. file's name: var filename = file.TMPFilenamePATH; // Get the stream to the file's content: var stream = File.OpenRead(filename); // Register this open stream: niiRequest.AddProbablyOpenStream(stream); // Store the file's length: file.setContentLength((int)stream.Length); // Store the stream: file.setInputStream(stream); // Store this file to the result: result.Add(file); } return(new NoIISFileCollection(result)); }
protected override bool Process(IInteraction parameters) { bool successful = true; IInteraction httpInteractionCandidate; if (successful = parameters.TryGetClosest(typeof(IHttpInteraction), out httpInteractionCandidate)) { IHttpInteraction httpInteraction = (IHttpInteraction)httpInteractionCandidate; if (httpInteraction.ContentType.StartsWith(MimeTypePrefix)) { string headerBoundary = httpInteraction.ContentType.Substring(MimeTypePrefix.Length); SimpleInteraction mappedValues = new SimpleInteraction(parameters); StreamingMultipartFormDataParser parser = new StreamingMultipartFormDataParser( httpInteraction.IncomingBody, headerBoundary, httpInteraction.Encoding, this.BufferSize ); parser.ParameterHandler += delegate(ParameterPart part) { if (this.StringFieldWhiteList.Contains(part.Name)) { mappedValues[part.Name] = part.Data; successful &= this.Branches.Get(part.Name, Stub).TryProcess( new WwwInputInteraction(part.Name, part.Data, parameters) ); } }; parser.FileHandler += delegate( string name, string fileName, string contentType, string contentDisposition, byte[] buffer, int bytes ) { if (this.StringFieldWhiteList.Contains(name)) { MemoryStream stream = new MemoryStream(buffer, 0, bytes); SimpleIncomingInteraction incoming = new SimpleIncomingInteraction( stream, mappedValues, contentDisposition, contentType ); incoming ["mimetype"] = contentType; incoming ["filename"] = fileName; successful &= this.Branches.Get(name, Stub).TryProcess(incoming); stream.Dispose(); } }; parser.Run(); successful &= this.Mapped.TryProcess(mappedValues); } else { Secretary.Report(5, "Require boundary content type for multipart forms"); successful &= Failure.TryProcess(parameters); } } else { Secretary.Report(5, "Require HttpInteraction for multipart forms"); successful &= Failure.TryProcess(parameters); } return(successful); }