Normally, one would use FormData
to compose the postData
of a request, but unfortunately we cannot do so here, as there is currently no way to get the stream (and other information) from a FormData
instance (nsIXHRSendable
is not scriptable, unfortunately), so we'll have to create a multipart/form-data
stream ourselves.
Since it is likely you'll want to post some file data as well, I added file uploads as well. ;)
function encodeFormData(data, charset) {
let encoder = Cc["@mozilla.org/intl/saveascharset;1"].
createInstance(Ci.nsISaveAsCharset);
encoder.Init(charset || "utf-8",
Ci.nsISaveAsCharset.attr_EntityAfterCharsetConv +
Ci.nsISaveAsCharset.attr_FallbackDecimalNCR,
0);
let encode = function(val, header) {
val = encoder.Convert(val);
if (header) {
val = val.replace(/
/g, " ").replace(/"/g, """);
}
return val;
}
let boundary = "----boundary--" + Date.now();
let mpis = Cc['@mozilla.org/io/multiplex-input-stream;1'].
createInstance(Ci.nsIMultiplexInputStream);
let item = "";
for (let k of Object.keys(data)) {
item += "--" + boundary + "
";
let v = data[k];
if (v instanceof Ci.nsIFile) {
let fstream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
fstream.init(v, -1, -1, Ci.nsIFileInputStream.DEFER_OPEN);
item += "Content-Disposition: form-data; name="" + encode(k, true) + "";" +
" filename="" + encode(v.leafName, true) + ""
";
let ctype = "application/octet-stream";
try {
let mime = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
ctype = mime.getTypeFromFile(v) || ctype;
}
catch (ex) {
console.warn("failed to get type", ex);
}
item += "Content-Type: " + ctype + "
";
let ss = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
ss.data = item;
mpis.appendStream(ss);
mpis.appendStream(fstream);
item = "";
}
else {
item += "Content-Disposition: form-data; name="" + encode(k, true) + ""
";
item += encode(v);
}
item += "
";
}
item += "--" + boundary + "--
";
let ss = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
ss.data = item;
mpis.appendStream(ss);
let postStream = Cc["@mozilla.org/network/mime-input-stream;1"].
createInstance(Ci.nsIMIMEInputStream);
postStream.addHeader("Content-Type",
"multipart/form-data; boundary=" + boundary);
postStream.setData(mpis);
postStream.addContentLength = true;
return postStream;
}
(You could use additional nsIMIMEInputStream
instead of string-concatenating stuff together, but this would perform worse and has no real merit).
Which can then be used like e.g.:
let file = Services.dirsvc.get("Desk", Ci.nsIFile);
file.append("australis-xp ha?llow, wo?rld.png");
let postData = encodeFormData({
"filename": "",
"image_url": "",
"image_content": "--somne value ---",
"contents": file
}, "iso8859-1");
gBrowser.loadOneTab("http://www.example.org/", {
inBackground: false,
postData: postData
});
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…