开发者

CouchDB/Cradle How do I add images?

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜