עזרה עם שגיאה בהעלאת קובץ בחלקים לימות המשיח בc#
-
תגובה: העלאת קובץ בapi לימות המשיח | קוד שבפייתון עובד וב nodejs לא כל כך
כתבתי את המחלקה הזו,
הקובץ המועלה - ריק,כשהוספתי דיבאג להעלאת החלקים, אני מקבל אחרי כל חלק את השגיאה:
Failed to upload chunk 1: {"error":"Server error. Uploads directory isn't writable","uploadName":null}
זה המחלקה,
(השוותי ידנית וגם ניסיתי עם קלוד, למחלקות בפייתון וnode שפורסמו שם, ללא הצלחה)using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Threading.Tasks; using System; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Windows.Forms; public class YemotUploader { // הגדרה אחת בלבד של גודל הצ'אנק private static readonly int ChunkSizeBytes = 8000000; // 8MB private byte[] ReadFileBytes(string filePath) { return File.ReadAllBytes(filePath); } private List<byte[]> ReadInChunks(string filePath) { var fileBytes = ReadFileBytes(filePath); var chunks = new List<byte[]>(); for (int offset = 0; offset < fileBytes.Length; offset += ChunkSizeBytes) { int currentChunkSize = Math.Min(ChunkSizeBytes, fileBytes.Length - offset); var chunk = new byte[currentChunkSize]; Array.Copy(fileBytes, offset, chunk, 0, currentChunkSize); chunks.Add(chunk); } return chunks; } public async Task<string> UploadFileAsync(string filePath, string tokenYemot, string path, string convertAudio, string autoNumbering) { var fileInfo = new FileInfo(filePath); var contentName = Path.GetFileName(filePath); var contentSize = fileInfo.Length; if (contentSize <= ChunkSizeBytes) { // העלאת קובץ קטן return await UploadSmallFileAsync(filePath, tokenYemot, path, convertAudio, autoNumbering); } else { // העלאת קובץ גדול return await UploadLargeFileAsync(filePath, tokenYemot, path, convertAudio, autoNumbering); } } private async Task<string> UploadSmallFileAsync(string filePath, string tokenYemot, string path, string convertAudio, string autoNumbering) { using (var httpClient = new HttpClient()) using (var formData = new MultipartFormDataContent()) { formData.Add(new StringContent(tokenYemot), "token"); formData.Add(new StringContent(path), "path"); formData.Add(new StringContent(convertAudio), "convertAudio"); formData.Add(new StringContent(autoNumbering), "autoNumbering"); var fileBytes = File.ReadAllBytes(filePath); var fileContent = new ByteArrayContent(fileBytes); fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data") { Name = "\"file\"", FileName = $"\"{Path.GetFileName(filePath)}\"" }; fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream"); formData.Add(fileContent, "file"); var response = await httpClient.PostAsync("https://www.call2all.co.il/ym/api/UploadFile", formData); return await response.Content.ReadAsStringAsync(); } } private async Task<string> UploadLargeFileAsync(string filePath, string tokenYemot, string path, string convertAudio, string autoNumbering) { var contentName = Path.GetFileName(filePath); var contentSize = new FileInfo(filePath).Length; var qquuid = Guid.NewGuid().ToString(); var chunks = ReadInChunks(filePath); var failedChunks = new List<int>(); // העלאת כל הצ'אנקים for (int i = 0; i < chunks.Count; i++) { try { using (var httpClient = new HttpClient()) using (var content = new MultipartFormDataContent()) { var chunk = chunks[i]; var offset = i * ChunkSizeBytes; content.Add(new StringContent(tokenYemot), "token"); content.Add(new StringContent(path), "path"); content.Add(new StringContent(qquuid), "qquuid"); content.Add(new StringContent(convertAudio), "convertAudio"); content.Add(new StringContent(autoNumbering), "autoNumbering"); content.Add(new StringContent("yemot-admin"), "uploader"); content.Add(new StringContent(contentName), "qqfilename"); content.Add(new StringContent(contentSize.ToString()), "qqtotalfilesize"); content.Add(new StringContent(chunks.Count.ToString()), "qqtotalparts"); content.Add(new StringContent(chunk.Length.ToString()), "qqchunksize"); content.Add(new StringContent(offset.ToString()), "qqpartbyteoffset"); content.Add(new StringContent(i.ToString()), "qqpartindex"); var fileContent = new ByteArrayContent(chunk); fileContent.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data") { Name = "\"qqfile\"", FileName = $"\"{contentName}\"" }; fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream"); content.Add(fileContent, "qqfile"); var response = await httpClient.PostAsync("https://www.call2all.co.il/ym/api/UploadFile", content); var responseText = await response.Content.ReadAsStringAsync(); // בדיקה האם התגובה תקינה if (!response.IsSuccessStatusCode || responseText.Contains("error")) { failedChunks.Add(i); MessageBox.Show($"Failed to upload chunk {i}: {responseText}"); Console.WriteLine($"Failed to upload chunk {i}: {responseText}"); } else { Console.WriteLine($"Successfully uploaded chunk {i}"); } } } catch (Exception ex) { failedChunks.Add(i); Console.WriteLine($"Error uploading chunk {i}: {ex.Message}"); } } // בדיקה האם כל הצ'אנקים הועלו בהצלחה if (failedChunks.Count > 0) { throw new Exception($"Failed to upload chunks: {string.Join(", ", failedChunks)}"); } // שליחת בקשת סיום using (var httpClient = new HttpClient()) { var finalFormData = new FormUrlEncodedContent(new Dictionary<string, string> { { "token", tokenYemot }, { "path", path }, { "uploader", "yemot-admin" }, { "convertAudio", convertAudio }, { "autoNumbering", autoNumbering }, { "qquuid", qquuid }, { "qqfilename", contentName }, { "qqtotalfilesize", contentSize.ToString() }, { "qqtotalparts", chunks.Count.ToString() } }); try { var doneResponse = await httpClient.PostAsync( "https://www.call2all.co.il/ym/api/UploadFile?done", finalFormData ); var responseText = await doneResponse.Content.ReadAsStringAsync(); Console.WriteLine($"Done response: {responseText}"); // ניסיון לפרסור של הjson הכפול if (!string.IsNullOrEmpty(responseText)) { // מציאת ה-JSON הראשון בתגובה int firstBrace = responseText.IndexOf('{'); int lastBrace = responseText.LastIndexOf('}'); if (firstBrace >= 0 && lastBrace > firstBrace) { string jsonPart = responseText.Substring(firstBrace, lastBrace - firstBrace + 1); try { // בדיקה האם ה-JSON תקין var jObject = JObject.Parse(jsonPart); return jsonPart; } catch (JsonReaderException) { Console.WriteLine($"Invalid JSON response: {jsonPart}"); } } } return responseText; } catch (Exception ex) { Console.WriteLine($"Error in done request: {ex.Message}"); throw; } } } }
-
@dovid שכחתי לציין במפורש, רק הלארג', הבעיה היא איפשהו בהעלאת החלקים,
גם כי העלאת קובץ גדול (עד 50) בUploadSmallFileAsync עובדת מצויין, וגם כי אני מקבל את השגיאה רק אחרי העלאת כל חלק, אבל בתגובה לבקשת סיום ההעלאה חוזרת תגובה של הצלחה מימות המשיח, (רק שהקובץ שנוצר ריק, כי אין חלקים..)
-
באמת על פניו נראה תמוה, אם תסכים לשתף אותי בטוקן שלך במייל אשמח.
בינתיים סידרתי את הקוד שיהיה יותר קל לבדוק:public class YemotUploader { // הגדרה אחת בלבד של גודל הצ'אנק public static readonly int ChunkSizeBytes = 8000000; // 8MB public YemotUploader(string fullFileName) { FileFullName = fullFileName; FileName = Path.GetFileName(fullFileName); FileSize = new FileInfo(FileFullName).Length; PartCount = (int)Math.Ceiling((double)FileSize / ChunkSizeBytes); } //static yemot setting public string tokenYemot { get; set; } public string path { get; set; } //preference per upload public bool convertAudio { get; set; } = false; public bool autoNumbering { get; set; } = false; //file prop public string FileFullName { get; set; } public string FileName { get; set; } public long FileSize { get; set; } //parts props public long PartSize { get; set; } public int PartCount { get; set; } public string qquuid { get; set; } public async Task<string> UploadFileAsync() { if (FileSize <= ChunkSizeBytes) return await UploadSmallFileAsync(); else return await UploadLargeFileAsync(); } private async Task<string> UploadSmallFileAsync() { using (var httpClient = new HttpClient()) using (var formData = new MultipartFormDataContent()) { AddGeneralInfo(formData); formData.Add(new ByteArrayContent(File.ReadAllBytes(FileFullName)), "file"); var response = await httpClient.PostAsync("https://www.call2all.co.il/ym/api/UploadFile", formData); return await response.Content.ReadAsStringAsync(); } } private async Task UploadPart(int index, long offset, byte[] part) { using (var content = new MultipartFormDataContent()) using (var httpClient = new HttpClient()) { AddGeneralInfo(content); content.Add(new StringContent("yemot-admin"), "uploader"); content.Add(new StringContent(qquuid), "qquuid"); content.Add(new StringContent(FileName), "qqfilename"); content.Add(new StringContent(FileSize.ToString()), "qqtotalfilesize"); content.Add(new StringContent(PartCount.ToString()), "qqtotalparts"); content.Add(new StringContent(PartSize.ToString()), "qqchunksize"); content.Add(new StringContent(offset.ToString()), "qqpartbyteoffset"); content.Add(new StringContent(index.ToString()), "qqpartindex"); content.Add(new ByteArrayContent(part), "qqfile"); var res = await httpClient.PostAsync("https://www.call2all.co.il/ym/api/UploadFile", content); Console.WriteLine($"Response part {index}: {await res.Content.ReadAsStringAsync()}"); } } private async Task<string> UploadLargeFileAsync() { var qquuid = Guid.NewGuid().ToString(); var index = 0; foreach (var chunk in ReadInChunks(FileFullName)) { await UploadPart(index, index * PartSize, chunk); index++; } using (var httpClient = new HttpClient()) { var finalFormData = new FormUrlEncodedContent(new Dictionary<string, string> { { "token", tokenYemot }, { "path", path }, { "uploader", "yemot-admin" }, { "convertAudio", convertAudio ? "1" :"0" }, { "autoNumbering", autoNumbering ? "1" :"0" }, { "qquuid", qquuid }, { "qqfilename", FileName }, { "qqtotalfilesize", FileSize.ToString() }, { "qqtotalparts", PartCount.ToString() } }); try { var doneResponse = await httpClient.PostAsync( "https://www.call2all.co.il/ym/api/UploadFile?done", finalFormData ); var responseText = await doneResponse.Content.ReadAsStringAsync(); Console.WriteLine($"Done response: {responseText}"); return responseText; } catch(System.Net.WebException ex) { Console.WriteLine($"Error in done request: {ex.Message}"); //get response var responseText = new StreamReader(ex.Response.GetResponseStream()).ReadToEnd(); Console.WriteLine($"Response: {responseText}"); throw; } catch (Exception ex) { Console.WriteLine($"Error in done request: {ex.Message}"); throw; } } } private void AddGeneralInfo(MultipartFormDataContent content) { content.Add(new StringContent(tokenYemot), "token"); content.Add(new StringContent(path), "path"); content.Add(new StringContent(convertAudio ? "1" : "0"), "convertAudio"); content.Add(new StringContent(autoNumbering ? "1" : "0"), "autoNumbering"); } private IEnumerable<byte[]> ReadInChunks(string filePath) { using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { var buffer = new byte[ChunkSizeBytes]; int bytesRead; while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0) yield return buffer.Take(bytesRead).ToArray(); } } }
-
איזה path כתבת?
-
curl ^"https://private.call2all.co.il/ym/api//UploadFile^" ^ -H ^"Accept: application/json^" ^ -H ^"Accept-Language: he-IL,he;q=0.9^" ^ -H ^"Cache-Control: no-cache^" ^ -H ^"Connection: keep-alive^" ^ -H ^"Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryIMbkXAPvau1ArzUz^" ^ -H ^"Cookie: XXXXXXXXXX -H ^"DNT: 1^" ^ -H ^"Origin: https://private.call2all.co.il^" ^ -H ^"Referer: https://private.call2all.co.il/yemot-admin-g1/^" ^ -H ^"Sec-Fetch-Dest: empty^" ^ -H ^"Sec-Fetch-Mode: cors^" ^ -H ^"Sec-Fetch-Site: same-origin^" ^ -H ^"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36^" ^ -H ^"X-Requested-With: XMLHttpRequest^" ^ -H ^"sec-ch-ua: ^\^"Chromium^\^";v=^\^"130^\^", ^\^"Google Chrome^\^";v=^\^"130^\^", ^\^"Not?A_Brand^\^";v=^\^"99^\^"^" ^ -H ^"sec-ch-ua-mobile: ?0^" ^ -H ^"sec-ch-ua-platform: ^\^"Windows^\^"^" ^ --data-raw ^"------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"uploader^\^"^ ^ yemot-admin^ ------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"token^\^"^ ^ XXXXXXXXXXXXXXXXX^ ------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"path^\^"^ ^ ivr/22/^ ------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"convertAudio^\^"^ ^ 0^ ------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"autoNumbering^\^"^ ^ true^ ------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"qqpartindex^\^"^ ^ 0^ ------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"qqpartbyteoffset^\^"^ ^ 0^ ------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"qqchunksize^\^"^ ^ 4000000^ ------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"qqtotalparts^\^"^ ^ 18^ ------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"qqtotalfilesize^\^"^ ^ 70504094^ ------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"qqfilename^\^"^ ^ ^מ^פ^ע^ל ^ה^פ^ו^ר^מ^ט^י^ם ^מ^מ^ז^ג ^א^ו^ד^י^ו ^ה^ק^ל^ט^ה ^א^ר^ו^כ^ה+^ה^ק^ל^ט^ה ^א^ר^ו^כ^ה+^ש^מ^ח^ת ^ח^ג+^ש^ח^ר ^ק^מ^ת^י.wav^ ------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"qquuid^\^"^ ^ 908c3432-3081-45ee-b778-91e1248902f4^ ------WebKitFormBoundaryIMbkXAPvau1ArzUz^ Content-Disposition: form-data; name=^\^"qqfile^\^"; filename=^\^"blob^\^"^ Content-Type: application/octet-stream^ ^ RIFF^^Î3^WAVEfmt ^^
מעניין שבתגובה התקינה, הערך שחוזר בשגיאה כnull, חוזר כnull גם כן.. (ייתכן שאין קשר, והשגיאה בכלל לא קשורה לערך הזה..)
{ "success": true, "uuid": "908c3432-3081-45ee-b778-91e1248902f4", "uploadName": null }