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

112 行
3.6KB

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