So Basically I want to let the user have the option of uploading an image when they register. I have no idea where to start forever. I know that CouchDB supports attachments, but how exactly does that work with Cradle.
I found the following code in Cradle's documentation
saveAttachment: function (/* id, [rev], attachmentName, contentType, dataOrStream */) {
So I know it can save attachments. How would I pass in the image then? I'm assuming that in the html, i have to use
form(action='/upload', enctype='multipart/form-data', method='post')
input(type='file', name='upload')
input(type='submit', value='Upload')
But where do I go from there? Wouldn't t开发者_开发技巧his step save the image on the server somewhere. Then do I somehow need to get the address of the image and pass that to cradle to save it as an attachment in the CouchDB database.
Thanks in advance if you can help me out!
You need to take the incoming stream from the form and then send a stream to CouchDB via Cradle.
Sending the stream to Cradle is probably the easy bit. This example shows how to do it with a local file:
db.saveAttachment(
doc.id,
doc.rev,
attachmentId,
mimetype,
fs.createReadStream(path),
function( err, data ){
console.log(data);
}
);
The trickier bit in my opinion is managing incoming files. They arrive as a multipart stream rather than being saved to a file. My preference would be to outsource that code to formidable, either directly, or indirectly via connect-form if you're using Connect or Express.
My current connect-form code can be summarised to this:
req.form.complete(function(err, fields, files){
if ( err ) // handle err
else
{
db.saveAttachment(
doc.id,
doc.rev,
attachmentId,
mimetype,
fs.createReadStream(files.name),
function( err, data ){
console.log(data);
}
);
}
});
This isn't optimal for speed as it creates an actual file on disk rather than streaming data from one place to the other, but it is convenient and may satisfy a lot of use cases.
Another package you should be aware of if you're dealing with image upload is node-imagemagick, which as you might expect from the name is the node.js wrapper for ImageMagick.
I wrote up some documentation on attachments that hopefully will be merged into the cradle readme soon. For now though here is the relevant section
Attachments
Cradle supports writing, reading, and removing attachments. The read and write operations can be either buffered or streaming
Writing
You can buffer the entire attachment body and send it all at once as a single request. The callback function will fire after the attachment upload is complete or an error occurs
Syntax
db.saveAttachment(idData, attachmentData, callbackFunction)
Example Say you want to save a text document as an attachment with the name 'fooAttachment.txt' and the content 'Foo document text'
var doc = <some existing document>
var id = doc._id
var rev = doc._rev
var idAndRevData = {
id: id,
rev: rev
}
var attachmentData = {
name: 'fooAttachment.txt',
'Content-Type': 'text/plain',
body: 'Foo document text'
}
db.saveAttachment(idAndRevData, attachmentData, function (err, reply) {
if (err) {
console.dir(err)
return
}
console.dir(reply)
})
Streaming
You can use a read stream to upload the attachment body rather than buffering the entire body first. The callback function will fire after the streaming upload completes or an error occurs
Syntax
var doc = savedDoc // <some saved couchdb document which has an attachment>
var id = doc._id
var rev = doc._rev
var idAndRevData = {
id: id,
rev: rev
}
var attachmentData = {
name: attachmentName // something like 'foo.txt'
'Content-Type': attachmentMimeType // something like 'text/plain', 'application/pdf', etc.
body: rawAttachmentBody // something like 'foo document body text'
}
var readStream = fs.createReadStream('/path/to/file/')
var writeStream = db.saveAttachment(idData, attachmentData, callbackFunction)
readStream.pipe(writeStream)
When the streaming upload is complete the callback function will fire
Example Attach a pdf file with the name 'bar.pdf' located at path './data/bar.pdf' to an existing document
var path = require('path')
var fs = require('fs')
// this document should already be saved in the couchdb database
var doc = {
_id: 'fooDocumentID',
_rev: 'fooDocumentRev'
}
var idData = {
id: doc._id,
rev: doc._rev
}
var filename = 'bar.pdf' // this is the filename that will be used in couchdb. It can be different from your source filename if desired
var filePath = path.join(__dirname, 'data', 'bar.pdf')
var readStream = fs.createReadStream
// note that there is no body field here since we are streaming the upload
var attachmentData = {
name: 'fooAttachment.txt',
'Content-Type': 'text/plain'
}
db.saveAttachment(idData, attachmentData, function (err, reply) {
if (err) {
console.dir(err)
return
}
console.dir(reply)
}, readStream)
Reading
Buffered
You can buffer the entire attachment and receive it all at once. The callback function will fire after the download is complete or an error occurs. The second parameter in the callback will be the binary data of the attachment
Syntax
db.getAttachment(documentID, attachmentName, callbackFunction)
Example Say you want to read back an attachment that was saved with the name 'foo.txt'
var doc = <some saved document that has an attachment with name *foo.txt*>
var id = doc._id
var attachmentName = 'foo.txt'
db.getAttachment(id, attachmentName, function (err, reply) {
if (err) {
console.dir(err)
return
}
console.dir(reply)
})
Streaming
You can stream the attachment as well. If the attachment is large it can be useful to stream it to limit memory consumption. The callback function will fire once the download stream is complete. Note that there is only a single error parameter passed to the callback function. The error is null is no errors occured or an error object if there was an error downloading the attachment. There is no second parameter containing the attachment data like in the buffered read example
Syntax
var readStream = db.getAttachment(documentID, attachmentName, callbackFunction)
Example Say you want to read back an attachment that was saved with the name 'foo.txt'. However the attachment foo.txt is very large so you want to stream it to disk rather than buffer the entire file into memory
var doc = <some saved document that has an attachment with name *foo.txt*>
var id = doc._id
var attachmentName = 'foo.txt'
var downloadPath = path.join(__dirname, 'foo_download.txt')
var writeStream = fs.createWriteStream(downloadPath)
var readStream = db.getAttachment('piped-attachment', 'foo.txt', function (err) { // note no second reply paramter
if (err) {
console.dir(err)
return
}
console.dir('download completed and written to file on disk at path', downloadPath)
})
readStream.pipe(writeStream)
Removing
You can remove uploaded attachments with a _id and an attachment name
Syntax
db.removeAttachment(documentID, attachmentName, callbackFunction)
Example Say you want to remove an attachment that was saved with the name 'foo.txt'
var doc = <some saved document that has an attachment with name *foo.txt*>
var id = doc._id
var attachmentName = 'foo.txt'
db.removeAttachment(id, attachmentName, function (err, reply) {
if (err) {
console.dir(err)
return
}
console.dir(reply)
})
FYI, for future readers, the calling parameters have changed since then, so this appears to be no longer valid. Check the source as the documentation doesn't describe how to use it.
精彩评论