PDA

View Full Version : سوال: نحوه عملکرد ، Ajax File Upload به همراه Progress Bar



raravaice
شنبه 21 اردیبهشت 1387, 16:55 عصر
سلام

دوستان من در این زمینه فقط نحوه عملکرد رو میخوام بدونم نیاز به کامپوننت ندارم.

میخوام بدونم فایلی که داره برای یه صفحه ارسال میشه چطور میشه فهمید که تا این لحظه چه حجمی از فایل آپلود شده.
نا گفته نمونه من به یه سری نتایج هم رسیدم و اون اینکه : از طریق جاوا اسکریپت میان فایل رو به صورت stream باز میکنن و با base64 تیکه تیکه میفرستن رجوع شود به
http://www.codeproject.com/KB/aspnet/AJAXUpload.aspx
و از طریق یه asmx یا تو نمونه های دیگه ashx میان وضعیت آپلود فایل رو کنترل میکنن.
ولی مشکل اینه که توی نمونه ای که من دیدم از ActiveXObject-ADODB.Stream استفاده شده فقط تو IE موجوده.

---------------------------
صورت مبهم کار اینه که در زمان آپلود فایل اینا چطور میان وضعیت فایل رو بررسی میکنن که تا کجا آپلود شده.

با تشکر

Behrouz_Rad
شنبه 21 اردیبهشت 1387, 18:27 عصر
سوال بسیار خوبی پرسیدی. من مدتی روی این قضیه کار کردم و به نتایج جالبی رسیدم. کامپوننتی هم بدین منظور ایجاد کردم و چند تا از بچه های همین فروم هم اون رو تست کردن. البته عمومیش نکردم. read on more!
اون چیزی که پیدا کردی رو فراموش کن ;)

تمامی روال کار با کلاس پایه ی HttpWorkerRequest و در روال BeginRequest انجام میشه.
همون طور که چندی پیش در یکی از پست هام گفتم: هر داده ای که به سرور فرستاده میشه، قسمت قسمت ارسال میشه.
اولین بخش داده با متد GetPreloadedEntityBody به دست میاد. این متد به طور پیش فرض 48 کیلو بایت رو بازیابی می کنه و در صورت بیشتر بودن حجم درخواست، (این مورد رو می تونی با متد IsEntireEntityBodyIsPreloaded متوجه بشی) بقیه رو با متد ReadEntityBody می خونی.
این توضیح خیلی خلاصه، چند تا نکته ی مهم رو در خودش داره که واسه اینکه مثل من این راه رو دوباره طی نکنی میگم...

اگر بخوای چنین کامپوننتی رو پیاده سازی کنی، در حقیقت معنای "آپلود" دیگه وجود حقیقی نداره. یعنی اینکه مثلا کنترل Input دیگه معنای خودش رو از دست میده و متد Save وجود نداره! کل جریان ارسال داده رو خودت بر عهده می گیری و تمام اون رو "یا" در یک فایل ذخیره می کنی "یا" بررسی می کنی که آیا داده ی ارسال شده یک فایل هست یا نه.
طبق استاندارد RFC، هر فایل ارسالی به سرور یک BOUNDRY داره. این BOUNDRY مشخص کننده ی ابتدا و انتهای فایل ارسال شده است. یک BOUNDRY می تونه به شکل ذیل باشه:


-----------------------------2720269396196

در داده های ارسالی وجود چنین سینتکسی رو بررسی و در صورت وجود می تونی متوجه بشی که کاربر فایلی رو آپلود کرده. نام فایل و دکمه ی هم که باعث آپلود شده در قسمت Content-Disposition و قبل از BOUNDRY وجود دارند.
بایت های ارسالی که حاوی محتوای فایل ارسال شده هستند بین BOUNDRY قرار می گیرن که با هر روشی که بلدی باید اونها رو استخراج و در یک فایل جدید ذخیره کنی. فایل تو آماده است!
نکته ی بعد اینکه چون با Stream کار می کنی حتما باید متد Flush رو در هر Cycle بازیابی داده فراخوانی کنی، در غیر اینصورت خطای Connection Reset دریافت می کنی... البته خطای واقعی رو در Event Viewer می تونی ببینی.
اون خطا این هست:


aspnet_wp.exe (PID: 772) was recycled because memory consumption exceeded the 600 MB (60 percent of available RAM).
Server Application Unavailable

مثلا RAM من 1 گیگا بایت هست و در ASP.NET در حالت لوکال فقط مجاز به مصرف 60 درصد RAM هستی! حالا فرض کن روی سرور چقدر اجازه ی استفاده از RAM رو بهت میدن!!!
بنابراین مرتبا باید متد Flush رو فراخونی کنی تا داده های دریافت شده به طور متناوب بر روی دیسک ذخیره بشن و RAM رو اشغال نکنن.
نکته ی بعد این هست که حتما برنامه رو باید با IIS اجرا کنی! اگر با وب سرور داخلی ASP.NET برنامه رو اجرا کنی، VS قفل می کنه! البته دلیلش منطقیه...
هر درخواستی بعد از اینکه به طور کامل دریافت شد، برای اجرا به IIS یا بهتر بگم به فیلترهای IIS پاس داده میشه. اگر برنامه رو با وب سرور داخلی ASP.NET اجرا کنی، برخی درخواست های مرتبط با HttpWorkerRequest که برای اجرا نیاز مبرم به وجود IIS دارند نمی تونن اجرا بشن!
نکته ی دیگه اینکه باید از AJAX و هسته ی اصلی اون بدین منظور استفاده کنی.
و مهم ترین نکته، ارسال بقیه ی درخواست ها (به جز BOUNDRY) به IIS هست که در این مورد باید از Reflection استفاده کنی.
فعلا در همین حد فکر می کنم کافی باشه.
در مورد هر قسمتی ازش که سوال داشتی بپرس...

اگر جستجو کنی چند تا کامپوننت و سورس برای آپلود با نمایش درصد و زمان پیدا می کنی...

موفق باشید.

raravaice
یک شنبه 22 اردیبهشت 1387, 15:57 عصر
سلام

لطفت کم نشه بهروز جان.
چندتا سئوال :



اولین بخش داده با متد GetPreloadedEntityBody به دست میاد. این متد به طور پیش فرض 48 کیلو بایت رو بازیابی می کنه1.یعنی اگر فایل ما مثلا 2KB باشه محتوی اون توی این قسمت قرار میگیره پس من تا 48KB اول رو نمیتونم با Progress نمایش بدم و یک دفعه بعد از تموم شدن کار باید برم روی 100% ؟

2.چه قاعده خاصی برای تفکیک BOUNDARY از محتوای فایل وجود داره؟!
آیا چیزی برای این مورد پیش بینی شده؟

3.مدت زمان executionTimeout در httpRuntime باید چند تعریف بشه که تا مادامی که فایل کامل ارسال نشده به کار خودش ادامه بده و Request timed out به من نده. آیا اصلا لازمه که این تعریف صورت بگیره؟



Event Type: Warning
Event Source: ASP.NET 2.0.50727.0
Event Category: Web Event
Event ID: 1309
Date: 5/11/2008
Time: 4:16:41 PM
User: N/A
Computer: ِDiamond
Description:
Event code: 3001
Event message: The request has been aborted.
Event time: 5/11/2008 4:16:41 PM
Event time (UTC): 5/11/2008 12:46:41 PM
Event ID: 143c24a2336b4e83ad8bc95cb0ba446e
Event sequence: 96
Event occurrence: 3
Event detail code: 0

Application information:
Application domain: /LM/W3SVC/945817168/Root-1-128549829100156250
Trust level: Full
Application Virtual Path: /
Application Path: D:\DOMAINS\lge.com\
Machine name: Diamond

Process information:
Process ID: 4524
Process name: w3wp.exe
Account name: NT AUTHORITY\NETWORK SERVICE

Exception information:
Exception type: HttpException
Exception message: Request timed out.

Request information:
Request URL: http://lge.com/Default.aspx?guid=88
Request path: /Default.aspx
User host address: 192.168.1.3
User:
Is authenticated: False
Authentication Type:
Thread account name: NT AUTHORITY\NETWORK SERVICE

Thread information:
Thread ID: 6
Thread account name: NT AUTHORITY\NETWORK SERVICE
Is impersonating: False
Stack trace:


بازم ممنون از لطفت
موفق باشید

Behrouz_Rad
یک شنبه 22 اردیبهشت 1387, 22:18 عصر
اگر فایل ما مثلا 2KB باشه محتوی اون توی این قسمت قرار میگیره پس من تا 48KB اول رو نمیتونم با Progress نمایش بدم و یک دفعه بعد از تموم شدن کار باید برم روی 100% ؟

بله دقیقا. این 48 کیلو بایت، بافر پیش فرض IIS برای خوندن اولین قسمت داده های ارسال شده است.


چه قاعده خاصی برای تفکیک BOUNDARY از محتوای فایل وجود داره؟!
آیا چیزی برای این مورد پیش بینی شده؟

قاعده ی خاصی نداره.
هر آنچه که بین BOUNDARY ابتدا و انتها قرار می گیره، محتویات فایل ارسال شده است.
مثلا با IndexOf و SubString می تونی بازیابیش کنی!


مدت زمان executionTimeout در httpRuntime باید چند تعریف بشه که تا مادامی که فایل کامل ارسال نشده به کار خودش ادامه بده و Request timed out به من نده. آیا اصلا لازمه که این تعریف صورت بگیره؟

بله باید تعریف بشه اما مشکلی که وجود داره این هست که اکثر هاست ها این امکان رو بهت نمیدن که درخواستی با هر حجم دلخواهی رو ارسال کنی؛ حتی با تعیین executionTimeout و maxHttpRequestLength !

raravaice
دوشنبه 23 اردیبهشت 1387, 10:19 صبح
ای کاش این اجنبی ها هم مثل شما به نکات اشاره میکردن. ما توی یکی از این فروم های خارجی این سئوال رو مطرح کردیم هر چی کامپوننت بود داشتن میدادن به خورد ما.;)

فقط چند جاش برام مبهم مونده.


باید از AJAX و هسته ی اصلی اون بدین منظور استفاده کنی.
مگه input file رو میشه با ajax پست کرد ؟


مهم ترین نکته، ارسال بقیه ی درخواست ها (به جز BOUNDRY) به IIS هست که در این مورد باید از Reflection استفاده کنی.

1.دلیل انجام این کار چیه؟
2. چرا "(به جز BOUNDRY)"؟

ممنون
موفق باشید

Behrouz_Rad
دوشنبه 23 اردیبهشت 1387, 11:13 صبح
مگه input file رو میشه با ajax پست کرد ؟

نه نمیشه. همون طور که می دونی با AJAX فقط داده های از نوع "رشته" رو میشه ارسال کرد.
به همین منظور، این نوع کامپوننت ها در یک IFrame قرار می گیرن و المان های نمایش درصد آپلود و زمان طی شده و ... در خارج از IFrame قرار داده میشن و با AJAX مرتبا به سرور درخواست ارسال می کنن و اطلاعات مورد نظر رو از سرور دریافت و نمایش میدن.



1.دلیل انجام این کار چیه؟

همون طور که گفتم، کل جریان ارسال درخواست رو خودت هندل می کنی.
یعنی IIS و ASP.NET هیچ دخالتی در اجرای روال های صفحه ندارن! مثلا Click دکمه بی معناست یا مثلا روال RowDataBound کنترل GridView بی معناست و هر چیز دیگه...
این به این خاطر هست که کل درخواست باید به سرور ارسال بشه، در حافظه بافر بشه و سپس توسط IIS به فیلترهای ASP.NET پاس داده میشه. وقتی که تو مانع از این کار میشی و کل درخواست رو خودت به شیوه ی خودت "بافر" می کنی، باید این درخواست ها (داده های ارسال شده) رو به IIS بدی تا اصطلاحا مراحل Pipeline به درستی طی بشن و روال Click، RowDataBound و ... معنا پیدا کنن.
4 تا فیلد هستن که برای Reflection در IIS باید به اونها مقداردهی کنی.
فیلد های contentAvailLength، _contentTotalLength، _preloadedContent_ و preloadedContentRead_



2. چرا "(به جز BOUNDRY)"؟

چون تو قسمتی که نیاز داشتی رو از درخواست برداشتی و لزومی به ارسال این مقدار اضافه به IIS که بعضا ممکنه زیاد هم باشه نداری!

موفق باشید.

raravaice
سه شنبه 24 اردیبهشت 1387, 19:18 عصر
سلام

سر نخهای عالی بهم دادی بازم ممنون.

بهروز جان من این برنامه رو برای multi file نوشتم عالی کار میکنه ولی بی انصاف وقتی شروع میکنه به کار کردن 4 دستی میچسبه به cpu چیکارش میشه کرد این مسئله رو که یه دفعه کل application رو از کار نندازه؟!
thread.sleep گذاشتم تو حلقه با request time out که بهت گفتم مواجه شدم و وقتی که چند کاربر با هم مشغول آپلود میشن app کاملا از کار میافته و w3wp کرش میکنه.
بافر ذخیره هم 16384 بایت در نظر گرفتم که کاملا معقول هست از نظر من!
چی پیشنهاد میدی؟!


موفق باشید

Behrouz_Rad
چهارشنبه 25 اردیبهشت 1387, 12:53 عصر
کدهات رو بگذار ببینم.

raravaice
چهارشنبه 25 اردیبهشت 1387, 19:35 عصر
آقا من سورس ماژول و برنامه آماده اجرا رو گذاشتم تو ftp

روی هاست مجبور شدم :


<trust level="Full"/>
چون تو level های پایین تر بهم جواب نمی داد.

پوشه آپلود هم پوشه ایه که temp اونجا ساخته میشه.
پوشه httpupload ماژول کامپایل نشده است.

موفق باشید

Behrouz_Rad
چهارشنبه 25 اردیبهشت 1387, 20:48 عصر
fileStream_ در متد ProcessBuffer بعد از پایان کارش در هر مرحله Close نشده.
بافر 16 کیلوبایت هم زیاده. اون رو 1024 بایت در نظر بگیر.
به نظر نمیرسه مشکل خاص دیگه ای وجود داشته باشه.

در خط آخر چرا Redirect کردی؟
ضمنا، چرا فایل ها رو در یک پوشه ی مخصوص ذخیره می کنی؟ ممکنه من دستوراتی نوشته باشم که بر مبنای اون بخوام با فایل آپلود شده کار خاصی در مسیر خاصی انجام بدم!

موفق باشید.

raravaice
چهارشنبه 25 اردیبهشت 1387, 21:19 عصر
fileStream_ در متد ProcessBuffer بعد از پایان کارش در هر مرحله Close نشده.پس mode فایل رو append کنم؟!
من در انتها که آپلود تموم بشه میبندم اینجوری چه مشکلاتی پیش میاره؟


در خط آخر چرا Redirect کردی؟اسم فایل هایی که آپلودشون تموم میشه با کوری میفرستم برای صفحه تا اونجا با اسم واقعی توی جای مشخص خودش ذخیره بشه و فایل temp پاک بشه.


ضمنا، چرا فایل ها رو در یک پوشه ی مخصوص ذخیره می کنی؟ ممکنه من دستوراتی نوشته باشم که بر مبنای اون بخوام با فایل آپلود شده کار خاصی در مسیر خاصی انجام بدم!هدف من ساخت کامپوننت نیست برای یکی از سایتهام میخوام که با این روش جلوی ذخیره فایلهای ناقصی که توسط کاربر cancel شده یا هر مشکل دیگه میگیرم.


به نظر نمیرسه مشکل خاص دیگه ای وجود داشته باشه.مشکل cpu usage هست که وقتی چندتا کاربر با هم مشغول آپلود میشن میاد رو 98% و بقیه قسمتهای app یا از کار میافته یا application در بعضی مواقع restart میشه و دیتای داخل application ها از بین میره.
این مسئله رو چیکار میشه کرد؟!
راهی داره؟!

موفق باشید

Behrouz_Rad
پنج شنبه 26 اردیبهشت 1387, 01:50 صبح
پس mode فایل رو append کنم؟!

بله.


من در انتها که آپلود تموم بشه میبندم اینجوری چه مشکلاتی پیش میاره؟

منابع اختصاص داده شده به Stream باز می مونن. حافظه مصرف میشه.


برای یکی از سایتهام میخوام که با این روش جلوی ذخیره فایلهای ناقصی که توسط کاربر cancel شده یا هر مشکل دیگه میگیرم.

مگه اگر کاربر در حین آپلود فایل، کار رو کنسل کنه، قسمتی از فایل آپلود میشه؟


مشکل cpu usage هست که وقتی چندتا کاربر با هم مشغول آپلود میشن میاد رو 98% و بقیه قسمتهای app یا از کار میافته یا application در بعضی مواقع restart میشه و دیتای داخل application ها از بین میره.

به جز اون 2 موردی که گفتم موردی رو ندیدم. کلا این روش برای جلوگیری از مصرف حافظه و درگیری کمتر CPU استفاده میشه. چون داده ها رو Chunk Chunk دریافت و پردازش می کنه.

موفق باشید.

raravaice
پنج شنبه 26 اردیبهشت 1387, 10:44 صبح
مگه اگر کاربر در حین آپلود فایل، کار رو کنسل کنه، قسمتی از فایل آپلود میشه؟

همونطور که گفتی من به جای اینکه کل فایل رو ببرم تو Stream و در انتها ذخیره کنم دارم در انتهای هر بافر 16K یا 1024B که شما گفتی ذخیره میکنم که در این حین امکان داره درخواست از سوی Client به هر نحوی کنسل بشه "بهترین مثال بسته شدن مرورگر" پس آپلود فایل ناقص میمونه و اون فایل به درد نمیخوره برای همین این کارو کردم که وقتی آپلود کامل تموم شد نام فایلهای ایجاد شده داخل temp ارسال میشه برای صفحه frame و اونجا با نام اصلی تو محل مورد نظر ذخیره میشه و فایل temp پاک میشه!

--------------------
برای رابط Ajax تو این مورد از asmx استفاده بشه کاراییش بیشتره یا ashx?

-------------------------------------------------

در پایان بازم ازت تشکر میکنم و اینکه از نظر شما مانعی نداره من این سورس رو اینجا بزارم تا اینم بشه یکی از منابع این سایت؟

موفق باشید

Behrouz_Rad
پنج شنبه 26 اردیبهشت 1387, 13:41 عصر
همونطور که گفتی من به جای اینکه کل فایل رو ببرم تو Stream و در انتها ذخیره کنم دارم در انتهای هر بافر 16K یا 1024B که شما گفتی ذخیره میکنم که در این حین امکان داره درخواست از سوی Client به هر نحوی کنسل بشه "بهترین مثال بسته شدن مرورگر" پس آپلود فایل ناقص میمونه و اون فایل به درد نمیخوره برای همین این کارو کردم که وقتی آپلود کامل تموم شد نام فایلهای ایجاد شده داخل temp ارسال میشه برای صفحه frame و اونجا با نام اصلی تو محل مورد نظر ذخیره میشه و فایل temp پاک میشه!

شما دلیل پیاده سازی چنین سیستمی رو بدین شکل ذکر کردی:


با این روش جلوی ذخیره فایلهای ناقصی که توسط کاربر cancel شده یا هر مشکل دیگه میگیرم.

که البته این حرف اشتباست!

بگذریم...



برای رابط Ajax تو این مورد از asmx استفاده بشه کاراییش بیشتره یا ashx?

از ashx استفاده کن.


از نظر شما مانعی نداره من این سورس رو اینجا بزارم تا اینم بشه یکی از منابع این سایت؟

با ذکر منبع، هر چه از دوست رسد خوش است :)

eAmin
یک شنبه 19 آبان 1387, 18:29 عصر
اولین بخش داده با متد GetPreloadedEntityBody به دست میاد. این متد به طور پیش فرض 48 کیلو بایت رو بازیابی می کنه

سلام.

اینی که نقل قول کردم هنوز برام مبهمه!!!
یه سری به سایت زیر بزنید:


http://mattberseth.com/blog/2008/07/aspnet_file_upload_with_realti.html
http://mattberseth2.com/demo/Default.aspx?Name=ASP.NET+File+Upload+with+*Real-Time*+Progress+Bar&Filter=All

این آقا چطوری می تونه بایت به بایت فایلها رو Upload کنه در صورتی که در نقل قول بالا ما فقط 48 کیلوبایت رو در دسترس داریم؟
در ضمن آیا می شه این مقدار پیش فرض رو که 48 کیلوبایت هست، تغییر داد و کمترش کرد؟

raravaice
شنبه 25 آبان 1387, 23:57 عصر
اینی که نقل قول کردم هنوز برام مبهمه!!!
.
.
این آقا چطوری می تونه بایت به بایت فایلها رو Upload کنه در صورتی که در نقل قول بالا ما فقط 48 کیلوبایت رو در دسترس داریم؟
در ضمن آیا می شه این مقدار پیش فرض رو که 48 کیلوبایت هست، تغییر داد و کمترش کرد؟

فقط 48 کیلو بایت در دسترس نیست امین جان!
48 کیلوبایت بافر اولیه هست که به طور پیش فرض توسط IIS برای سرآیند ها در نظر گرفته میشه!

نمایش دادن نمادین Progress برای حجم های زیر 48 کیلوبایت قابل انجام هست ولی اولین دریافت شما 48 کیلوبایت حجم داره.

موفق باشید

Neo Persian
سه شنبه 26 آذر 1387, 19:08 عصر
من مدتيه روي موضوع اين تاپيك دارم كار مي كنم
يه سري مشكلات دارم:
1. اگه فايل آپلود شده بزرگتر از 48KB يا همون PreloadedEntityBody باشه Browser در حالت آپلود ميمونه و اتفاقي نمي افته و وقتي stop زده بشه همون 48kb آپلود ميشه!
2. علاوه بر محتويات فايل، مقادير ViewState, كنترلهاي صفحه و ... هم درون فايل آپلود شده قرار ميگيره. نحوه جداسازي فايل از بقيه عناصر رو موفق نشدم عملي كنم
كدهايي كه زدم رو قرار ميدم
دوستان لطف مي كنن ايرادهاشو بهم بگن


HttpApplication application = (HttpApplication)sender;
HttpWorkerRequest request = (HttpWorkerRequest)application.Context.GetType().G etProperty("WorkerRequest", (BindingFlags)36).GetValue(application.Context, null);
if (application.Context.Request.ContentType.IndexOf("multipart/form-data") > -1)
{
string guid = Guid.NewGuid().ToString();
string filename = application.Context.Server.MapPath("./") + guid + ".tmp";
if (request.HasEntityBody())
{
int content_length = Convert.ToInt32(request.GetKnownRequestHeader(Http WorkerRequest.HeaderContentLength));
int content_received = 0;
FileStream newFile = new FileStream(filename, FileMode.Create);
byte[] body = request.GetPreloadedEntityBody();
content_received += body.Length;
newFile.Write(body, 0, body.Length);
if (!request.IsEntireEntityBodyIsPreloaded())
{
byte[] a_buffer = new byte[1024];
int bytes_read = 1024;
while ((content_length - content_received) >= bytes_read)
{
bytes_read = request.ReadEntityBody(a_buffer, a_buffer.Length);
content_received += bytes_read;
newFile.Write(a_buffer, 0, bytes_read);
newFile.Flush();
}
bytes_read = request.ReadEntityBody(a_buffer, (content_length - content_received));
newFile.Write(a_buffer, 0, bytes_read);
content_received += bytes_read;
}
newFile.Close();
}
}

ParsaGostar
شنبه 18 دی 1389, 20:02 عصر
هیچ کدوم از دوستان نتونست عملی boundarray را از فایل جدا کنند ؟

من حدود دو هفته است روی این موضوع دارم کار می کنم ولی به نتیجه نرسیدم

لطفا اگر شما تونستین عملی نتیجه بگیرید اطلاع دهید