如何从带附件的SOAP响应中提取附件并保存为文件?
Hey there! Let's sort out how to properly grab that attachment from your SOAP response. The main issue with your current code is that you're using StreamReader (a text-focused reader) to process the entire response—this destroys binary data like your PDF, since text encoding can't preserve all byte values correctly. Plus, you need to parse the MIME multi-part structure instead of treating everything as a single string.
Step-by-Step Fix
First, let's break down what you need to do:
- Parse the MIME boundary from the response headers
- Process the raw response stream (not as text)
- Identify the attachment part using its
Content-IDorContent-Type - Extract the binary content and save it to a file
Option 1: Manual MIME Parsing (No External Libraries)
Here's a working implementation that handles the MIME structure manually:
using (WebResponse response = request.GetResponse()) { // Get the MIME boundary from the Content-Type header var contentType = response.Headers["Content-Type"]; var boundary = contentType.Split(';').FirstOrDefault(p => p.Trim().StartsWith("boundary="))? .Split('=')[1].Trim().Replace("\"", ""); if (string.IsNullOrEmpty(boundary)) throw new InvalidOperationException("Could not find MIME boundary in response headers"); using (var responseStream = response.GetResponseStream()) using (var reader = new BinaryReader(responseStream)) { var boundaryBytes = Encoding.ASCII.GetBytes($"--{boundary}"); byte[] buffer = new byte[4096]; int bytesRead; bool isAttachmentPart = false; bool skipHeaders = false; using (var fileStream = new FileStream(HttpContext.Current.Server.MapPath("/test.pdf"), FileMode.Create)) { while ((bytesRead = reader.Read(buffer, 0, buffer.Length)) > 0) { // Check if we've hit a boundary var boundaryIndex = FindBufferIndex(buffer, bytesRead, boundaryBytes); if (boundaryIndex != -1) { isAttachmentPart = false; skipHeaders = false; // Read past the boundary and check the next part's headers var remainingBytes = bytesRead - boundaryIndex - boundaryBytes.Length; if (remainingBytes > 0) { // Process the remaining data to check headers var headerBuffer = new byte[remainingBytes]; Array.Copy(buffer, boundaryIndex + boundaryBytes.Length, headerBuffer, 0, remainingBytes); var headerText = Encoding.ASCII.GetString(headerBuffer); // Check if this part is the attachment (match Content-ID or Content-Type) if (headerText.Contains("Content-ID: <urn:uuid:C455EAC131FBC506CE1521805985219>") || headerText.Contains("Content-Type: application/octet-stream")) { isAttachmentPart = true; skipHeaders = true; } } } else if (isAttachmentPart) { if (skipHeaders) { // Skip the headers (look for the empty line that separates headers from content) var emptyLineIndex = FindBufferIndex(buffer, bytesRead, Encoding.ASCII.GetBytes("\r\n\r\n")); if (emptyLineIndex != -1) { skipHeaders = false; // Write the content after the empty line fileStream.Write(buffer, emptyLineIndex + 4, bytesRead - emptyLineIndex - 4); } } else { // Write the binary content to the file fileStream.Write(buffer, 0, bytesRead); } } } } } } // Helper method to find a byte sequence in a buffer private static int FindBufferIndex(byte[] buffer, int bufferLength, byte[] searchBytes) { for (int i = 0; i <= bufferLength - searchBytes.Length; i++) { bool match = true; for (int j = 0; j < searchBytes.Length; j++) { if (buffer[i + j] != searchBytes[j]) { match = false; break; } } if (match) return i; } return -1; }
Key Notes for Manual Parsing:
- We use
BinaryReaderinstead ofStreamReaderto handle raw bytes without encoding corruption - The MIME boundary is extracted from the
Content-Typeheader of the response - We scan the stream to find each boundary, then check the part headers to identify the attachment
- We skip the headers (up to the empty line) before writing the binary content to the file
Option 2: Use a Library (Simpler & More Reliable)
Manual MIME parsing can get messy (handling different encodings, line endings, etc.). A better approach is to use a dedicated MIME parsing library like MimeKit:
- First, install the MimeKit NuGet package:
Install-Package MimeKit
- Then use this code to parse the response:
using (WebResponse response = request.GetResponse()) using (var responseStream = response.GetResponseStream()) { // Parse the entire MIME message var message = MimeMessage.Load(responseStream); // Find the attachment part (match by Content-ID or Content-Type) var attachment = message.BodyParts.OfType<MimePart>().FirstOrDefault(part => part.ContentId == "<urn:uuid:C455EAC131FBC506CE1521805985219>" || part.ContentType.MimeType == "application/octet-stream"); if (attachment != null) { // Save the attachment to file using (var fileStream = new FileStream(HttpContext.Current.Server.MapPath("/test.pdf"), FileMode.Create)) { attachment.Content.DecodeTo(fileStream); } } }
This is way cleaner and handles all edge cases (like different content transfer encodings) automatically.
Why Your Original Code Failed:
StreamReader.ReadToEnd()converts the binary attachment to a string using ASCII encoding, which replaces unrepresentable bytes with?—permanently corrupting the PDF data- You tried converting this corrupted string back to bytes and treating it as Base64, but the attachment wasn't Base64 encoded (it's marked as
binarytransfer encoding)
内容的提问来源于stack exchange,提问作者Dhaval Kateliya

