העלאת קבצים ללא קומפוננטה
לתסכולי הרב כאשר אני חיפשתי ברשת כיצד להעלות קבצים לשרת ללא רכיבים מוכנים, לא מצאתי מספיק מידע. רוב המידע היה לא מוסבר בצורה מספיק ברורה וכלל בעיקר קוד שלא הבנתי. המאמר הזה בא לפתור את הבעיה הזאת לכם, אני מקווה שעשיתי עבודה טובה.
בכדי לשלוח מידע אל השרת אנו משתמשים בטפסים בהם יש שדות מסוגים שונים, בכדי לשלוח קובץ עלינו להוסיף לטופס שדה מסוג קובץ. הנה כך:1 <input type="file" name="f1">
הבעיה הראשונה שנתקלים בה במצב הזה היא שכל מה שנשלח אל השרת הוא שם הקובץ, בכדי לפתור זאת עלינו לשנות את הקידוד של הטפס. את זאת אנו עושים על ידי קביעת התכונה enctype ל multipart/form-data, הנה כך: 1 <form action="page2.asp" method="post" enctype="multipart/form-data">
כמו כן שיטת השליחה חייבת להיות post בשביל לשלוח קבצים.
כעת אנו מוכנים לטפל בצד המקבל.
עד היום נהגתם בוודאי להשתמש באוספים Request.QueryString ו Request.Form בכדי לקבל ערכים שנשלחו אל הדף שלכם, עבור קבלת קבצים שני אלה לא עוזרים לנו. איך בכל זאת ניתן לקבל את המידע שנשלח אלינו?! בעזרת שיטה שנקראת Request.BinaryRead ותכונה שנקראת Request.TotalBytes. מה שBinaryRead עושה זה לקרוא את כל המידע שנשלח אלינו כגוש של בתים לא מעובד, היא מקבלת פרמטר אחד שאומר לה כמה בתים לקרוא. TotalBytes מחזירה לנו כמה בתים סך הכל נשלחו אלינו, ולכן אם נכתוב את הקוד הבא, נקבל את כל המידע שנשלח: 1
2
3
4 <%
dim binData
binData=Request.BinaryRead(Request.TotalBytes)
%>
הבעיה הבאה העומדת בפנינו היא שהמידע הוא לא מעובד לחלוטין וקשה לעשות עליו מוניפילציות בצורה זו, יהיה קל הרבה יותר לטפל הוא אם נעביר את המידע לצורת מחרוזת. את זה מדגים הקוד הבא: 1
2
3
4
5
6
7
8
9 <%
Dim I,char1,strData
For I=0 To LenB(binData) Step 1
char1=MidB(binData, I, 1)
char1=AscB(char1)
char1=chr(char1)
strData=strData & char1
Next
%>
הקוד הזה הוא הדרך הפשוטה ביותר לביצוע הפעולה, עם זאת הוא לא כלכך יעיל ובמקרה של שליחת קבצים גדולים ייתכן והוא ייקח הרבה זמן לביצוע ולכן לא מומלץ לשתמש בו, במקום כדאי להשתמש ביכולות המובנות של אובייקט ה RecordSet שהוא אובייקט שתוכנת מחוץ לסביבת ASP ולכן מהיר בהרבה. הצורה להמיר את המידע הבינארי למחרוזת בעזרת RecordSet מודגמת בקוד הבא: 1
2
3
4
5
6
7
8
9
10
11
12
13
14 <%
Const adLongVarChar=201
dim binData,strData,rs1
binData=Request.BinaryRead(Request.TotalBytes)
set rs1=Server.CreateObject("ADODB.RecordSet")
rs1.Fields.Append "theData",adLongVarChar,LenB(binData)
rs1.Open
rs1.AddNew
rs1.Fields("theData").AppendChunk(binData)
rs1.Update
strData=rs1.Fields("theData").value
set rs1=Nothing
%>
כעת יש לנו את המידע בצורה של טקסט ונוכל להדפיס אותו ולהסתכל מה קיבלנו בצורה הזאת: 1 <%Response.Write "<pre>" & strData & "</pre>"%>
התוצאה של כזאת הדפסה בד"כ נראית משהו כזה:
—————————–23281168279961 Content-Disposition: form-data; name="t1" Hello World!!!!! —————————–23281168279961 Content-Disposition: form-data; name="f1"; filename="gur-flights.txt" Content-Type: text/plain /______________________________ |Lufthansa flights on 1/9/2004:| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/ TK1954 Amsterdam–>Istanbul TK1192 Istanbul–>Tel-Aviv Query number #96523. /_____ |temp:| ~~~~~/ KL461 —————————–23281168279961
כעת עלינו לנתח את התוצאה הזאת בכדי להבין מה אנחנו רואים. דבר ראשון אנו רואים שורה ארוכה של מקפים ואז איזשהו מספר ואז ירידת שורה, לאחר מכן Content-Disposition: form-data; מה שנראה כמו איזה מזהה של סוג המידע ואז name="t1" זה כבר נראה כמו שם של שדה בטופס. עכשיו שתי ירידות שורה ואז הטקסט עצמו שהיה בשדה הזה ואז עוד ירידת שורה והשורה עם ההרבה מקפים שוב.
תרשו לי להבהיר לכם מה אנו רואים, יש מפריד בין שדה לשדה, ירידת שורה, מידע על השדה, שתי ירידות שורה ואז המידע שהמשתמש שם בשדה, ירידת שורה ושוב המפריד. כך למעשה יש עבור כל שדה בטופס, ולכן לא יהיה קשה ליצור פונקציה שתחפש בטקטס הזה את שם השדה שאנו צריכים ותיתן לנו את הערך שלו.
אז לעבודה, דבר ראשון בוא נשמור את המפריד לשימוש בפונקציה. היות והמפריד הוא מתחילת המידע עד הירידת שורה הראשונה, אז זאת לא ממש בעיה. הנה כך: 1
2
3
4
5 <%
Dim bounder,bounderEnd
bounderEnd=instr(1,strData,vbNewLine)-Len(vbNewLine)
bounder=mid(strData,1,bouderEnd)
%>
כעת שיש לנו את המפריד נוכל לכתוב את הפונקציה שצריכה לקבל את שם השדה ולחפש שדה,שדה, כלומר בין כל שני מפרידים לבדוק את השם של השדה ואז אם הוא שווה לשם שאנו מחפשים לרדת שתי שורות שם תהיה התחלת המידע ואז לעלות שתי שורות מעל המפריד הבא שם יהיה סוף המידע. הנה הקוד: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 <%
Function GetPostedData(fieldName)
Dim CurPos,CurName,EndPos
CurPos=0
Do While CurName<>fieldName'כל עוד השם שונה ממה שאנו מחפשים
'קבע את המיקום הנוכחי אחרי המפריד הקרוב
CurPos=instr(CurPos+1,strData,bounder)+len(bounder)
'קדם את מיקומינו הנוכחי עד השדה שם
CurPos=instr(CurPos,strData,"name=")
'אם לא נמצא שדה שם, כלומר הגענו לסוף המידע
'החזר מחרוזת ריקה, כלומר לא נמצא המידע בעל שם זה
If CurPos=0 Then
GetPostedData=""
Exit Function
End If
'קדם את מיקומינו הנוכחי עד אחרי הגרשיים הקרובים
CurPos=instr(CurPos+1,strData,chr(34))+1 ' chr(34)="
'מצא את מיקום אחד לפני הגרשיים הבאים
EndPos=instr(CurPos,strData,chr(34))-1
'מה שיש בין שתי נקודות אלו הוא השם של השדה הנוכחי
CurName=mid(strData,CurPos,EndPos-CurPos+1)
'תנאי הלולאה יוציא אותנו מהלולאה במקרה וזה השם שאנו מחפשים
Loop
dim DataPos,DataEndPos
'מצא את מיקום סוף המידע
DataEndPos=instr(CurPos,strData,bounder)-3
DataPos=instr(CurPos,strData,"Content-Type")
'מצא את מיקום תחילת המידע ע"י ירידת שתי שורות
'אם יש שדה סוג קובץ אז לך אליו ואז רד שתי שורות
if DataPos=0 Or DataPos>DataEndPos Then
DataPos=instr(CurPos,strData,chr(13) & chr(10))
Else
DataPos=instr(DataPos,strData,chr(13) & chr(10))
End If
DataPos=DataPos+ 4
'החזרת את המידע שנמצא בין שתי הנקודות של התחלה וסוף שמצאנו
GetPostedData=mid(strData,DataPos,DataEndPos-DataPos+1)
End Function
%>
הקוד מוסבר היטב, אבל אולי שמתם לב שיש מצב בו יש יותר משורת מידע אחת עבור השדה, המצב הזה הוא בשדה קובץ. אם תלכו אחורה עכשיו ותראו את הפלט דוגמא שהדפסתי אז תראו שבשדה קובץ יש שורה נוספת שמפרטת את סוג הקובץ. הפונקציה הזאת בודקת אם קיימת השורה הזאת ובמידה שכן יורדת שורה נוספת בכדי לעבור גם אותה.
זאת פונקציח מצויינת שנותנת לנו תוכן של כל שדה כולל תוכן של קובץ במקרה של שדה קובץ כך שנוכל לשמור אותו אם אנו רוצים בכך, בכדי לעשות זאת נפתח קובץ חדש על השרת ונכתוב לתוכו את התוכן של הקובץ הנה כך: 1
2
3
4
5
6
7
8
9
10
11
12 <%
Dim File1,Fso1,FileName1
Const ForWriting=2
Set Fso1=Server.CreateObject("Scripting.FileSystemObject")
FileName1="file1"
Set File1=Fso1.OpenTextFile(Server.MapPath(".") & "" & FileName1,ForWriting,True)
File1.Write GetPostedData("f1")
Set File1=Nothing
Response.Write "File Writen!<br>"
Set Fso1=Nothing
%>
הקוד הזה טוב כל עוד אנחנו רוצים להמציא את השם של הקובץ, אבל לפעמים אנחנו רוצים לקחת את השם של הקובץ ששלחו לנו. פה אני אפנה אותכם שוב לפלט שהדפסתי קודם, שם אתם יכולים לראות שבשדה קובץ יש גם מידע על שם הקובץ תחת הכותרת filename לכן אנו יכולים ליצור פונקציה נוספת שעושה כמו קודמתה רק שבמקום להחזיר את התוכן של השדה, מחזירה את שם הקובץ. הנה כך: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 <%
Function GetPostedFileName(fieldName)
If fieldName="" Then
GetPostedFileName=""
Exit Function
End If
dim CurPos,CurName,CurEnd
CurPos=0
CurName=""
'חיפוש שם האלמנט כפי שעשינו בפונקציה הקודמת
Do While fieldName<>CurName
CurPos=instr(CurPos+1,strData,bounder)
CurPos=instr(CurPos,strData,"name=")
if CurPos=0 Then
GetPostedFileName=""
Exit Function
End If
CurPos=instr(CurPos,strData,chr(34))+1 ' chr(34)="
CurEnd=instr(CurPos,strData,chr(34))-1
CurName=mid(strData,curPos,CurEnd-CurPos+1)
Loop
'חיפוש שדה ה filename
CurPos=instr(CurEnd,strData,"filename=")
CurEnd=instr(CurEnd,strData,bounder)
'בדיקה שהשדה אינו של אלמנט אחר כלומר אינו אחרי המפריד
'ושהוא קיים בכלל…
If CurPos=0 Or CurPos>CurEnd Then
GetPostedFileName=""
Exit Function
End If
'מציאת המקומות אחרי ולפני הגרשיים של שם הקובץ
CurPos=instr(CurPos,strData,chr(34))+1
CurEnd=instr(CurPos,strData,chr(34))-1
If CurEnd<=CurPos Then
GetPostedFileName=""
Exit Function
End If
dim path
path=mid(strData,CurPos,CurEnd-CurPos+1)
'גזירת שם הקובץ מתוך המסלול המלא שנשלח
'(מה שנשלח הוא הנתיב המלא אל הקובץ)
path=StrReverse(path)
CurEnd=instr(1,path,"")-1
If CurEnd<>-1 Then
GetPostedFileName=StrReverse(mid(path,1,CurEnd))
Else
GetPostedFileName=StrReverse(path)
End If
End Function
%>
כך אנו יכולים לשמור על השרת את הקובץ בשם בו הוא נשלח אלינו, הנה הדוגמא הקודמת רק עם שימוש בשם של הקובץ: 1
2
3
4
5
6
7
8
9
10
11
12
13
14 <%
Dim File1,Fso1,FileName1
Const ForWriting=2
Set Fso1=Server.CreateObject("Scripting.FileSystemObject")
FileName1=GetPostedFileName("f1")
If FileName1<>"" Then
Set File1=Fso1.OpenTextFile(Server.MapPath(".") & "" & FileName1,ForWriting,True)
File1.Write GetPostedData("f1")
Set File1=Nothing
Response.Write "File Writen!<br>"
End If
Set Fso1=Nothing
%>
וזהו אני חושב שיש לכם את כל הכלים בכדי לעלות קובץ לשרת ללא רכיבים מוכנים.
אם יש איזשהן שאלות תמיד ניתן לפנות אליי בemail בכתובת [email protected] .
תגובות בפייסבוק