upload.ino 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /*
  2. Upload.ino
  3. This script handles file upload to the web-stick
  4. by default this function require authentication.
  5. Hence, admin.txt must be set before use
  6. */
  7. void handleFileUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
  8. // make sure authenticated before allowing upload
  9. if (IsUserAuthed(request)) {
  10. String logmessage = "";
  11. //String logmessage = "Client:" + request->client()->remoteIP().toString() + " " + request->url();
  12. //Serial.println(logmessage);
  13. //Rewrite the filename if it is too long
  14. filename = trimFilename(filename);
  15. //Get the dir to store the file
  16. String dirToStore = GetPara(request, "dir");
  17. if (!dirToStore.startsWith("/")) {
  18. dirToStore = "/" + dirToStore;
  19. }
  20. if (!dirToStore.endsWith("/")) {
  21. dirToStore = dirToStore + "/";
  22. }
  23. dirToStore = "/www" + dirToStore;
  24. if (!index) {
  25. Serial.println("Selected Upload Dir: " + dirToStore);
  26. logmessage = "Upload Start: " + String(filename) + " by " + request->client()->remoteIP().toString();
  27. // open the file on first call and store the file handle in the request object
  28. if (!SD.exists(dirToStore)) {
  29. SD.mkdir(dirToStore);
  30. }
  31. //Already exists. Overwrite
  32. if (SD.exists(dirToStore + filename)) {
  33. SD.remove(dirToStore + filename);
  34. }
  35. request->_tempFile = SD.open(dirToStore + filename, FILE_WRITE);
  36. Serial.println(logmessage);
  37. }
  38. if (len) {
  39. // stream the incoming chunk to the opened file
  40. request->_tempFile.write(data, len);
  41. //logmessage = "Writing file: " + String(filename) + " index=" + String(index) + " len=" + String(len);
  42. //Serial.println(logmessage);
  43. }
  44. if (final) {
  45. logmessage = "Upload Complete: " + String(filename) + ",size: " + String(index + len);
  46. // close the file handle as the upload is now done
  47. request->_tempFile.close();
  48. Serial.println(logmessage);
  49. //Check if the file actually exists on SD card
  50. if (!SD.exists(String(dirToStore + filename))) {
  51. //Not found!
  52. SendErrorResp(request, "Write failed for " + String(filename) + ". Try a shorter name!");
  53. return;
  54. }
  55. request->send(200, "application/json", "ok");
  56. }
  57. } else {
  58. Serial.println("Auth: Failed");
  59. SendErrorResp(request, "unauthorized");
  60. }
  61. }
  62. /*
  63. Upload File Trimming
  64. This trim the given uploading filename to less than 32 chars
  65. if the filename is too long to fit on the SD card.
  66. The code handle UTF-8 trimming at the bytes level.
  67. */
  68. //UTF-8 is varaible in length, this get how many bytes in the coming sequences
  69. //are part of this UTF-8 word
  70. uint8_t getUtf8CharLength(const uint8_t firstByte) {
  71. if ((firstByte & 0x80) == 0) {
  72. // Single-byte character
  73. return 1;
  74. } else if ((firstByte & 0xE0) == 0xC0) {
  75. // Two-byte character
  76. return 2;
  77. } else if ((firstByte & 0xF0) == 0xE0) {
  78. // Three-byte character
  79. return 3;
  80. } else if ((firstByte & 0xF8) == 0xF0) {
  81. // Four-byte character
  82. return 4;
  83. } else {
  84. // Invalid UTF-8 character
  85. return 0;
  86. }
  87. }
  88. String filterBrokenUtf8(const String& input) {
  89. String result;
  90. size_t inputLength = input.length();
  91. size_t i = 0;
  92. while (i < inputLength) {
  93. uint8_t firstByte = input[i];
  94. uint8_t charLength = getUtf8CharLength(firstByte);
  95. if (charLength == 0){
  96. //End of filter
  97. break;
  98. }
  99. // Check if the character is complete (non-broken UTF-8)
  100. if (i + charLength <= inputLength) {
  101. // Check for invalid UTF-8 continuation bytes in the character
  102. for (size_t j = 0; j < charLength; j++) {
  103. result += input[i];
  104. i++;
  105. }
  106. }else{
  107. //End of valid UTF-8 segment
  108. break;
  109. }
  110. }
  111. return result;
  112. }
  113. String trimFilename(String& filename) {
  114. //Replace all things that is not suppose to be in the filename
  115. filename.replace("#","");
  116. filename.replace("?","");
  117. filename.replace("&","");
  118. // Find the position of the last dot (file extension)
  119. int dotIndex = filename.lastIndexOf('.');
  120. // Check if the filename contains a dot and the extension is not at the beginning or end
  121. if (dotIndex > 0 && dotIndex < filename.length() - 1) {
  122. // Calculate the maximum length for the filename (excluding extension)
  123. int maxLength = 32 - (filename.length() - dotIndex - 1);
  124. // Truncate the filename if it's longer than the maximum length
  125. if (filename.length() > maxLength) {
  126. String trimmedFilename = filterBrokenUtf8(filename.substring(0, maxLength)) + filename.substring(dotIndex);
  127. return trimmedFilename;
  128. }
  129. }
  130. // If no truncation is needed, return the original filename
  131. return filename;
  132. }