最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - NodeExpress Generate a one time routelinkdownload? - Stack Overflow

programmeradmin4浏览0评论

How would I go about creating a one time download link in nodeJS or Express?

I'm trying to find the simplest way to accomplish this. My ideas so far are:

Use fs stream to read and then delete the file or Somehow generate a link/route that gets removed once the download button is clicked

Are any of these implementations possible? Is there a simpler way?

Any help or example code would be greatly appreciated!

-Thanks

How would I go about creating a one time download link in nodeJS or Express?

I'm trying to find the simplest way to accomplish this. My ideas so far are:

Use fs stream to read and then delete the file or Somehow generate a link/route that gets removed once the download button is clicked

Are any of these implementations possible? Is there a simpler way?

Any help or example code would be greatly appreciated!

-Thanks

Share Improve this question asked Feb 24, 2014 at 21:54 gbachikgbachik 1,7063 gold badges17 silver badges30 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 16

Check this simple implementation:

You store the information of the download in a file. The filename is the download session id. The file content is the real path of the file to be downloaded.

Use these three functions to manage the lifecycle of the download sessions:

var fs     = require('fs');
var crypto = require('crypto');
var path   = require('path');

// Path where we store the download sessions
const DL_SESSION_FOLDER = '/var/download_sessions';

/* Creates a download session */
function createDownload(filePath, callback) {
  // Check the existence of DL_SESSION_FOLDER
  if (!fs.existsSync(DL_SESSION_FOLDER)) return callback(new Error('Session directory does not exist'));

  // Check the existence of the file
  if (!fs.existsSync(filePath)) return callback(new Error('File doest not exist'));

  // Generate the download sid (session id)
  var downloadSid = crypto.createHash('md5').update(Math.random().toString()).digest('hex');

  // Generate the download session filename
  var dlSessionFileName = path.join(DL_SESSION_FOLDER, downloadSid + '.download');

  // Write the link of the file to the download session file
  fs.writeFile(dlSessionFileName, filePath, function(err) {
    if (err) return callback(err);

    // If succeeded, return the new download sid
    callback(null, downloadSid);
  });
}

/* Gets the download file path related to a download sid */
function getDownloadFilePath(downloadSid, callback) {
  // Get the download session file name
  var dlSessionFileName = path.join(DL_SESSION_FOLDER, downloadSid + '.download');

  // Check if the download session exists
  if (!fs.existsSync(dlSessionFileName)) return callback(new Error('Download does not exist'));

  // Get the file path
  fs.readFile(dlSessionFileName, function(err, data) {
    if (err) return callback(err);

    // Return the file path
    callback(null, data);
  });
}

/* Deletes a download session */
function deleteDownload(downloadSid, callback) {
  // Get the download session file name
  var dlSessionFileName = path.join(DL_SESSION_FOLDER, downloadSid + '.download');

  // Check if the download session exists
  if (!fs.existsSync(dlSessionFileName)) return callback(new Error('Download does not exist'));

  // Delete the download session
  fs.unlink(dlSessionFileName, function(err) {
    if (err) return callback(err);

    // Return success (no error)
    callback();
  });
}

Use createDownload() to create download sessions wherever you need to. It returns the download sid, then you can use it to build your download URL like: http://your.server.com/download?sid=<RETURNED SID>.

Finally you can add a simple handler to your /download route:

app.get('/download', function(req, res, next) {
  // Get the download sid
  var downloadSid = req.query.sid;

  // Get the download file path
  getDownloadFilePath(downloadSid, function(err, path) {
    if (err) return res.end('Error');

    // Read and send the file here...

    // Finally, delete the download session to invalidate the link
    deleteDownload(downloadSid, function(err) {
      // ...
    });
  });
});

With this method, you don't have to create/move/delete big download files, which could cause slow responses and unnecessary resource consumption.

You can delete routes from the app.routes object. See Remove route mappings in NodeJS Express for more info.

Here is my quick and not very well tested way of doing what you ask:

 var express = require('express');
 var app = express();

 app.get('/download', function(req,res,next){
    res.download('./path/to/your.file');

    //find this route and delete it.
    for(i = 0; i < app.routes.get.length; i++){
        if(app.routes.get[i].path === '/download'){
            app.routes.get.splice(i,1); 
        }
    }
});

app.listen(80);

I'd probably map a single route to manage downloads, and then upon downloading the file, move or delete it. That way I can prevent a lot of cashing of routes, or a lot of small temp files from the other two answers, but YMMV. Something like this:

// say your downloads are in /downloads
app.get('/dl/:filename', function(req, res) {
  var fileStream = fs.createReadStream('/downloads' + req.params.filename);
  // error handler, ie. file not there...
  fileStream.on('error', function(err) {
    if(err) {
      res.status(404); // or something
      return res.end();
    }
  });
  // here you ow pipe that stream to the response, 
  fileStream.on('data', downloadHandler);
  // and here delete the file or move it to other folder or whatever, do cleanup
  fileStream.on('end', deleteFileHandler);
}

Note: This is a possible security vulnerability, it could let the adversary download files outside your downloads location. That filename param is passed directly to fs.

发布评论

评论列表(0)

  1. 暂无评论