SOAP with Attachments(简称SwA)是一种将SOAP消息与二进制附件结合传输的规范。它并没有把附件内容直接写进XML信封,而是利用MIME多部分消息,将SOAP信封作为其中一个部分,把图片、文档等二进制数据作为另外的部分一起发送。这种方式在早期Web Service开发中常被用来规避Base64编码带来的膨胀问题。
SwA 的基本工作原理
在SwA模型中,最外层是一个MIME multipart/related消息。第一部分通常是text/xml类型的SOAP信封,其根元素中通过href属性引用附件的Content-ID,而不是把附件内容内联。后续部分则是各个附件,它们拥有独立的MIME类型和Content-ID头。接收方先解析MIME结构,取出SOAP部分做XML解析,再根据引用去匹配对应的附件部分。
这种设计的核心好处是SOAP处理器只需关心XML,不需要把几兆甚至几十兆的二进制数据先解码成文本再编码回字节。附件以原始二进制流动,节省CPU也减少内存峰值。下面的示例展示了一个SwA消息的简化MIME结构:
MIME-Version: 1.0
Content-Type: multipart/related; boundary=boundary123; type="text/xml"
--boundary123
Content-Type: text/xml; charset="utf-8"
Content-ID: <soap-part>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<UploadFile>
<file href="cid:file-attach"/>
</UploadFile>
</soap:Body>
</soap:Envelope>
--boundary123
Content-Type: application/pdf
Content-ID: <file-attach>
%PDF-1.4
... 二进制PDF数据 ...
--boundary123--
与 Base64 内联方式的对比
传统做法是把附件做Base64编码后直接放进SOAP体,例如使用xsd:base64Binary元素。这样做实现简单,所有SOAP工具链都支持,但编码后体积大约增加三分之一,并且解析时要把整段文本读入内存再解码。对于大文件,这会迅速拖垮服务端的吞吐能力。
SwA把附件移出XML,从体积和解析成本上都有优势。但代价是它要求传输协议和客户端都理解MIME multipart,并且SOAP栈要能正确处理Content-ID引用。在一些只支持纯XML SOAP的代理或网关上,SwA消息可能被误判或拦截。下表列出两者差异:
| 维度 | Base64内联 | SwA |
|---|---|---|
| 消息结构 | 单一XML | MIME多部分 |
| 体积 | 膨胀约33% | 接近原始大小 |
| 解析开销 | 高 | 低 |
| 兼容性 | 极好 | 依赖MIME支持 |
在代码中发送 SwA 消息
以Java的SAAJ(SOAP with Attachments API for Java)为例,我们可以手动构造带附件的SOAP消息。下面代码演示如何创建SOAP信封并添加一份文本附件,通过DataHandler关联:
import javax.xml.soap.*;
import javax.activation.*;
import java.io.*;
public class SwASender {
public static void main(String[] args) throws Exception {
// 创建SOAP连接与消息工厂
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage();
SOAPPart soapPart = msg.getSOAPPart();
SOAPEnvelope env = soapPart.getEnvelope();
SOAPBody body = env.getBody();
// 在Body中放置一个引用附件的元素
SOAPElement elem = body.addChildElement("SendDoc");
SOAPElement ref = elem.addChildElement("docRef");
ref.addAttribute(env.createName("href"), "cid:doc.txt");
// 添加附件,使用DataHandler包装字节流
byte[] data = "hello swa".getBytes("UTF-8");
DataHandler dh = new DataHandler(new ByteArrayDataSource(data, "text/plain"));
AttachmentPart attach = msg.createAttachmentPart(dh);
attach.setContentId("doc.txt");
msg.addAttachmentPart(attach);
msg.saveChanges();
msg.writeTo(new FileOutputStream("swa.msg"));
}
}
上面代码先建SOAP信封,再在Body里写了一个指向cid:doc.txt的引用,随后用AttachmentPart把字节作为附件挂到消息上。运行后会得到一个MIME格式的文件,第一部分为SOAP,第二部分为文本附件。实际发送时,只需把msg通过SOAPConnection提交到端点即可。
需要注意的是,AttachmentPart的Content-ID在引用时不要漏掉cid:前缀,否则接收端无法匹配。另外某些旧容器对Content-ID的格式要求严格,最好用尖括号包裹,如<doc.txt>,并在href里写cid:doc.txt。
SwA 的局限与 MTOM 的替代
SwA虽然解决了体积问题,但规范比较松散,各厂商对MIME边界、Content-ID解析存在细微差异。而且它不支持对附件做XML级别的签名或加密,安全模型偏弱。后来W3C推出了MTOM(SOAP Message Transmission Optimization Mechanism),同样用MIME分包,但把附件在XML里用xop:Include元素引用,语义更清晰,也能和WS-Security更好结合。
如今新系统多选用MTOM,但在对接老式银行、电信网关时,SwA仍广泛存在。理解它的封装与处理,不仅能帮你在遗留接口联调时少踩坑,也能更清楚Web Service附件传输的演进脉络。如果你的场景只是内部小文件且工具链统一,Base64内联反而最省心;一旦涉及大附件或跨组织旧系统,SwA和MTOM才值得认真考量。
SOAP_with_AttachmentsSwAMIME修改时间:2026-08-04 16:57:39