A simple file sharing site with an easy to use API and online panel.
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

108 行
3.5KB

  1. const Busboy = require('busboy');
  2. const is = require('type-is');
  3. const config = require('config');
  4. const authenticate = require('../auth/authenticate');
  5. const disk = require('./disk');
  6. const identifier = require('./id');
  7. const uploadMultipart = async (req, res, next) => {
  8. if (!is(req, ['multipart']))
  9. return res.status(400).json({message: 'Bad request.'});
  10. // Store whether the user has authenticated, because an api key might be included with the form later
  11. // If not authenticated with a session, we'll have to wait for key authentication from the multipart form data
  12. let status = await authenticate(req, 'file.upload');
  13. // Function to call once the file is sent or an error is encountered
  14. let isDone = false;
  15. const done = async (err, data) => {
  16. if (isDone) return;
  17. isDone = true;
  18. req.unpipe(busboy);
  19. busboy.removeAllListeners();
  20. req.on('readable', req.read.bind(req));
  21. if (err) {
  22. if (data.path)
  23. await disk.remove(data.path);
  24. if (err === 'LIMIT_FILE_SIZE')
  25. return res.status(413).json({message: 'File too large.'});
  26. else {
  27. console.log(err.stack);
  28. return res.status(500).json({message: 'Internal server error.'});
  29. }
  30. } else {
  31. req.file = data.file;
  32. next();
  33. }
  34. };
  35. // Create the busboy object to parse the multipart data
  36. const busboy = new Busboy({
  37. headers: req.headers,
  38. limits: {
  39. fileSize: config.get('Upload.maxSize')
  40. }
  41. });
  42. req.body = {};
  43. busboy.on('field', (fieldName, value) => {
  44. req.body[fieldName] = value;
  45. });
  46. let fileReceived = false;
  47. busboy.on('file', async (fieldName, stream, name, encoding, mime) => {
  48. // Only process one file, discard everything after that
  49. if (fileReceived)
  50. return req.unpipe(busboy);
  51. fileReceived = true;
  52. // If a key was encountered and we are not authenticated, try to authenticate with it before the final check
  53. if (req.body.key && !status.authenticated)
  54. status = await authenticate(req, 'file.upload', status);
  55. // Finally, check if we have auth before preceeding, keys should have been processed by now
  56. if (!status.authenticated)
  57. return res.status(401).json({message: 'Unauthorized.'});
  58. if (!status.permission)
  59. return res.status(403).json({message: 'Forbidden.'});
  60. // Don't attach to the files object if there is no file
  61. if (!name) return stream.resume();
  62. // Generate an ID for the file, saving it with that filename
  63. const path = config.get('Upload.path');
  64. const newName = await identifier.generate();
  65. const finalPath = path + '/' + newName;
  66. // Set the file attributes
  67. file = {
  68. originalName: name,
  69. name: newName,
  70. path: finalPath,
  71. encoding: encoding,
  72. mime: mime
  73. };
  74. // Ensure the output directory exists
  75. await disk.mkdir(path);
  76. // Handle errors
  77. stream.on('error', err => done(err, {path: finalPath}));
  78. stream.on('limit', () => done('LIMIT_FILE_SIZE', {path: finalPath}));
  79. file.size = await disk.write(finalPath, stream);
  80. await done(null, {file: file});
  81. });
  82. busboy.on('error', err => done(err));
  83. busboy.on('finished', () => done(null, {file: file}));
  84. req.pipe(busboy);
  85. };
  86. module.exports = uploadMultipart;