Опыт видео-конвертации
Надо сказать, что в природе существуют некие совершенно убогие видео-порталы, которые могут принимать загружаемые пользователями ролики только в одном формате, чаще всего FLV. Я изначально подходил к этому вопросу только так, чтобы принимать ролики произвольных форматов и чтобы мой портал сам выполнял конвертацию в нужный мне формат. Об этом аспекте построения видеохостингов я и собираюсь рассказать на этой страничке.
В 2009-м году я описал кое-какие свои мысли по построению видеохостингов (OpenSource-проекты для работы с видео и торрентами), однако с той поры много воды утекло и стало ясно, что бороться на них надо над теми проблемами, которых не было видно 2-3 года назад.
В качестве примера такой проблемы, которая возникает на практике и совсем не видна начинающему строителю видеохостингов - кратковременные пропадания сети в локалке (по загадочной причине), которые приводят к полной неработоспособности видеохостинга. Ведь задача конвертации видео должна размещаться на отдельной виртуалке (или отдельной физической машине, связанной с Web-порталом по локалке. Выполнять конвертации на одном кампутере с Web-порталом совершенно немыслимо - портал будет парализован задачей видео-конвертации (которая поставит процессор в 100% например на целый час.
Поэтому в результате практического опыта работы с видеоконвертерами у меня в коде моего конвертеров вокруг всех запросов в базу появилась вот такая обвязка ReConnect - которая не дает упасть конвертеру при проблемах в сети или недоступности SQL:
203: ...
204: Sub ReConnect()
205: Dim ReconnectCounter2 As Integer
206: Start2:
207: Try
208: If Browse_CN Is Nothing Then Browse_CN = New System.Data.SqlClient.SqlConnection(My.Settings.SQLServer_ConnectionStrings)
209: If Browse_CN.State <> ConnectionState.Open Then Browse_CN.Open()
210: If Browse_CMD Is Nothing Then Browse_CMD = New System.Data.SqlClient.SqlCommand(My.Settings.Browse_Query, Browse_CN)
211: Catch ex As System.Data.SqlClient.SqlException
212: ReconnectCounter2 += 1
213: If ReconnectCounter2 > ConverterControlParameters.Rows(0)("MaxReconnect") Then
214: Throw New Exception("Нет коннекта к базе " & vbCrLf & ex.Message)
215: Else
216: Threading.Thread.Sleep(CInt(ConverterControlParameters.Rows(0)("SurveyTime")) * 1000)
217: GoTo Start2
218: End If
219:
220: End Try
221: End Sub
222: ...
Подобных проблем накопилось за годы эксплуатации видеоконвертации много. Например поначалу я строил сами конвертеры с рабочим столом, запуская процесс видеоконвертации в BackgroundWorker. Однако, проходила неделя-другая и видеоковертер зависал. Я стал экспериментировать по-разному. В конце-концов когда я убрал BackgroundWorker и многократно все перепроверил - стало понятно что налицо очередное микрософтовское свинство. Где-то идет утечка памяти в BackgroundWorker и проги с ним не могут работать больше нескольких недель. Хотя у меня есть одна прога, которая работает обычно примерно месяц, но она не манипулирует потоками - WebDownloader_UltraLite - ваш личный поисковик по рунету с особыми возможностями поиска - и в этом случае BackgroundWorker работает нормально. Но при активной манипуляции потоками в BackgroundWorker - начинаются утечки памяти и больше двух недель прога не работает. Со временем изменился и сам комплект кодеков конвертации.
Вот так, собственно, у меня и накопился опыт построения видео-хостингов.
На этой страничке я не буду описывать свой видеоконвертер полностью в виде OpenSource, ибо это коммерческая разработка, я покажу лишь общий принцип его работы:
1: Sub Survey()
2: .....
3: While True
4: ....
5: OneConverting.GoCoder(Survey_RDR("i"), CInt(ConverterControlParameters.Rows(0)("SurveyTime")) * 1000, ConverterControlParameters.Rows(0)("Parameters"), ConverterControlParameters.Rows(0)("MaxReconnect"), CInt(ConverterControlParameters.Rows(0)("Timeout")) * 1000)
6: .....
7: 'ждем
8: Threading.Thread.Sleep(CInt(ConverterControlParameters.Rows(0)("SurveyTime")) * 1000)
9: End While
10: .....
11: End Sub
12:
13: Class OneConverting
14: Shared Sub GoCoder(ByVal i As Integer, ByVal SurveyTime As Integer, ByVal ConvertOptions As String, ByVal MaxReconnect As Integer, ByVal Timeout As Integer)
15: ....
16: 'запускаем собственно конвертацию
17: Dim MediaCoder As New System.Diagnostics.Process
18: MediaCoder.StartInfo.Arguments = " -i " & SharedSourceFileName & " " & ConvertOptions & " " & SharedTargetFileName '& " 2>""" & ErrLogFileName & """"
19: MediaCoder.StartInfo.FileName = System.IO.Path.Combine(My.Settings.MediaCoder_Directory, "ffmpeg.exe")
20: MediaCoder.StartInfo.WorkingDirectory = My.Settings.FLVFolder_ShareDiskName
21: MediaCoder.StartInfo.UseShellExecute = False
22: MediaCoder.StartInfo.RedirectStandardError = True
23: MediaCoder.Start()
24: MediaCoder.WaitForExit(Timeout)
25: OutputErrorStream = MediaCoder.StandardError
26: ErrText = OutputErrorStream.ReadToEnd
27: OutputErrorStream.Close()
28: MediaCoder.Close()
29: WriteError(i, ErrText)
30: ...
31: End Sub
32: End Class
Продолжая свой курс на поддержку OpenSource-программирования - я стараюсь описать небольшие части даже моих закрытых коммерческих разработок. Чтобы в интернете присутствовали хотя бы некоторые реальные, пусть даже небольшие, фрагменты кода реальных коммерческих разработок. Хотя я не буду описывать здесь весь конвертер или всю его админку, я тем опишу несколько компонентов админки моего видеоконвертера, носящих универсальный характер.
Только не путайте пользовательский интерфейс видеоконвертера (это интерфейс администратора видеохостинга) с тем что видит конечный пользователь, загружающий свои ролики на сайт. Что именно видит конечный пользователь - вы можете увидеть, зарегистрировавшись на любом моем видеохостинге, например video.votpusk.ru и добавив там свой ролик в произвольном видео-формате. А здесь я опишу то, что видит только администратор портала votpusk.ru в части управления моим видеоконвертером.
В других порталах я разбивал функционал управления видеоконвертером по-другому по пунктам меню - можно вообще все свести в один пункт, но для определенности остановимся на вот такой (более подробной разбивке функционала управления видеоконвертером).
На этом рисунке вы видите фрагмент моей админки портала votpusk.ru - зеленым тут отчеркнуты пять пунктов, что относится к видеоконвертеру:
Пункт "сконвертированы" это просто запросы в базу отражающие нормально сконвертированные ролики. Здесь администратор может поставить ролик на титульную страницу, как лучший ролик сайта или забанить его вообще. Пункт "конвертируются" отображает очереди на конвертацию. Пункт "несконвертированы" - это неудачно сконвертированные ролики с сообщением об ошибке. Здесь администратор может только удалить запись в базе о неудачном конвертировании ролика. Пункт видеофайлы - позволяет манипулировать отдельными файлами видеоконвертера - а не записыми в базе.
Как вы поняли, в этом моем решении видеофайлы хранятся не в базе, как например я описывал здесь (Cекционирование графики при SQL-хранении) - поэтому есть отдельно операции над записыми в базе и операции над различными файлами видеоконвертера.
Прежде всего посмотрим на фрейм - "управление процессом VideoConverter". Как вы понимаете, это именно фрейм - ибо расположен он на физической машине с видео-конвертером (в отличии от собственно Web-портала и админки Web-портала, которые размещены совсем на других кампутерах).
Это автономный проектик, состоящий из сайтика на две странички - login.aspx и Defaul.aspx. Обратите внимание, что этот сайт должен запускаться под учетной записью LocalSystem - иначе доступа к перечню процессов машины у него не будет.
Страничка Default.aspx Устроена так:
1: <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>
2:
3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4:
5: <html xmlns="http://www.w3.org/1999/xhtml">
6: <head id="Head1" runat="server">
7: <title>Управление процессом <asp:Literal ID="Literal1" runat="server" Text="<%$ appSettings:ProcessControlName %>"/></title>
8: </head>
9: <body>
10: <form id="form1" runat="server">
11: <div>
12: <h4>Управление процессом
13: <asp:Label ID="Label3" runat="server" Text='<%$ appSettings:ProcessControlName %>'></asp:Label>
14: </h4>
15:
16: <asp:LinkButton ID="LinkButton4" runat="server">Start</asp:LinkButton>
17: <asp:DataList ID="DataList1" runat="server" CellPadding="2" >
18: <ItemTemplate>
19: <asp:LinkButton ID="LinkButton1" runat="server" onclick="LinkButton1_Click" CommandArgument='<%# Eval("ID") %>'>Restart</asp:LinkButton>
20: </td><td>
21: <asp:LinkButton ID="LinkButton3" runat="server" onclick="LinkButton3_Click" CommandArgument='<%# Eval("ID") %>'>Kill</asp:LinkButton>
22: </td><td>
23: <asp:Label ID="Label1" runat="server" Text='<%# Eval("ID") %>' ></asp:Label>
24: </td><td>
25: <asp:Label ID="Label2" runat="server" Text='<%# Eval("StartTime") %>' ></asp:Label>
26: </ItemTemplate>
27: </asp:DataList>
28: <asp:LinkButton ID="LinkButton2" runat="server">Refresh</asp:LinkButton>
29: <br /><asp:Label ID="lErr1" runat="server" ForeColor="Red"></asp:Label>
30: </div>
31: </form>
32: </body>
33: </html>
1:
2: Partial Class _Default
3: Inherits System.Web.UI.Page
4:
5: Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
6: If Not IsPostBack Then
7: If Not Me.Page.User.Identity.IsAuthenticated Then
8: Response.Redirect("Login.aspx")
9: Exit Sub
10: End If
11: If Not Roles.IsUserInRole(Me.Page.User.Identity.Name, "UserAdmin") Then
12: Exit Sub
13: End If
14: End If
15: If Not IsPostBack Then
16: Refresh()
17: End If
18: End Sub
19:
20: Sub Refresh()
21: Dim X() As System.Diagnostics.Process = System.Diagnostics.Process.GetProcessesByName(System.Configuration.ConfigurationManager.AppSettings("ProcessControlName"))
22: DataList1.DataSource = X
23: DataList1.DataBind()
24: End Sub
25:
26:
27: Protected Sub LinkButton2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles LinkButton2.Click
28: Refresh()
29: End Sub
30:
31: Protected Sub LinkButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
32: lErr1.Text = ""
33: Dim LinkButton1 As LinkButton = CType(sender, LinkButton)
34: Dim PID As Integer = LinkButton1.CommandArgument
35: Try
36: Dim X As System.Diagnostics.Process = System.Diagnostics.Process.GetProcessById(PID)
37: X.Kill()
38: System.Diagnostics.Process.Start(System.Configuration.ConfigurationManager.AppSettings("ProcessStart"))
39: Catch ex As Exception
40: lErr1.Text = ex.Message
41: End Try
42: Refresh()
43: End Sub
44:
45:
46: Protected Sub LinkButton3_Click(ByVal sender As Object, ByVal e As System.EventArgs)
47: lErr1.Text = ""
48: Dim LinkButton1 As LinkButton = CType(sender, LinkButton)
49: Dim PID As Integer = LinkButton1.CommandArgument
50: Try
51: Dim X As System.Diagnostics.Process = System.Diagnostics.Process.GetProcessById(PID)
52: X.Kill()
53: Catch ex As Exception
54: lErr1.Text = ex.Message
55: End Try
56: Refresh()
57: End Sub
58:
59: Protected Sub LinkButton4_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles LinkButton4.Click
60: lErr1.Text = ""
61: Try
62: System.Diagnostics.Process.Start(System.Configuration.ConfigurationManager.AppSettings("ProcessStart"))
63: Catch ex As Exception
64: lErr1.Text = ex.Message
65: End Try
66: Refresh()
67: End Sub
68: End Class
Страничка Login.aspx совсем простая:
1: <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Login.aspx.vb" Inherits="Login" %>
2: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3:
4: <html xmlns="http://www.w3.org/1999/xhtml" >
5: <body >
6: <form id="form1" runat="server">
7: <div>
8: <asp:Login ID="Login1" runat="server" LoginButtonText="Войти" PasswordLabelText="Пароль:"
9: RememberMeText="Помнить меня" TitleText="Вход" UserNameLabelText="EMail:" DestinationPageUrl="Default.aspx" PasswordRecoveryText="Забыл пароль" PasswordRecoveryUrl="#">
10: <TitleTextStyle Font-Size="Small" />
11: <CheckBoxStyle Font-Size="Small" />
12: <LabelStyle Font-Size="Small" />
13: <HyperLinkStyle Font-Size="Small" />
14: <InstructionTextStyle Font-Size="Small" />
15: <TextBoxStyle Font-Size="Small" />
16: <ValidatorTextStyle Font-Size="Small" />
17: <FailureTextStyle Font-Size="Small" />
18: </asp:Login>
19: </div>
20: </form>
21: </body>
22: </html>
1: Partial Class login
2: Inherits System.Web.UI.Page
3:
4: Protected Sub Login1_Authenticate(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.AuthenticateEventArgs) Handles Login1.Authenticate
5: Login1.PasswordRecoveryUrl = "http://user.votpusk.ru/RestorePass.aspx"
6: If Membership.ValidateUser(Login1.UserName, Login1.Password) Then
7: e.Authenticated = True
8: If Login1.RememberMeSet Then
9: Dim ticket As New FormsAuthenticationTicket(Login1.UserName, True, 5000)
10: Dim EncryptTicket As String = FormsAuthentication.Encrypt(ticket)
11: Dim AUCook As HttpCookie = New HttpCookie(FormsAuthentication.FormsCookieName, EncryptTicket)
12: AUCook.Domain = "votpusk.ru"
13: Response.Cookies.Add(AUCook)
14: Else
15: FormsAuthentication.SetAuthCookie(Login1.UserName, False)
16: End If
17: Login1.DestinationPageUrl = "Default.aspx"
18: Else
19: Login1.DestinationPageUrl = "Login.aspx"
20: e.Authenticated = False
21: End If
22: '
23: 'признак аутентификации для клиентского скрипта
24: Dim AU_Cook As New HttpCookie("AU")
25: AU_Cook.Item("U") = 1
26: Response.Cookies.Set(AU_Cook)
27: ''
28: End Sub
29:
30: Protected Sub Login1_LoginError(ByVal sender As Object, ByVal e As System.EventArgs) Handles Login1.LoginError
31: FormsAuthentication.SignOut()
32: 'признак аутентификации для клиентского скрипта
33: Dim AU_Cook As New HttpCookie("AU")
34: AU_Cook.Item("U") = 0
35: Response.Cookies.Set(AU_Cook)
36: End Sub
37:
38: End Class
Для работы странички Login надо правильно сконфигурить Membership-провайдеры. Делается это так:
1: <?xml version="1.0"?>
2: <!--
3: Note: As an alternative to hand editing this file you can use the
4: web admin tool to configure settings for your application. Use
5: the Website->Asp.Net Configuration option in Visual Studio.
6: A full list of settings and comments can be found in
7: machine.config.comments usually located in
8: \Windows\Microsoft.Net\Framework\v2.x\Config
9: -->
10: <configuration>
11: <appSettings>
12: <add key="ProcessControlName" value="VideoConverter"/>
13: <add key="ProcessStart" value="J:\VideoConvert\VideoConverter.exe"/>
14: </appSettings>
15: <connectionStrings>
16: <!-- connectionStrings нужен тут только для аутентификации -->
17: <remove name="LocalSqlServer"/>
18: <add name="LocalSqlServer" connectionString="server=XXXXXX;Initial Catalog=YYYYYYYY;Max Pool Size=1000;User ID=VotpuskLogin;Password=ZZZZZZZZZ" providerName="System.Data.SqlClient"/>
19: </connectionStrings>
20: <system.web>
21: <!-- секции authentication,roleManager,membership нужны тут только для аутентификации -->
22: <authentication mode="Forms"/>
23: <roleManager enabled="true" defaultProvider="AspNetSqlProvider">
24: <providers>
25: <remove name="AspNetSqlRoleProvider"/>
26: <remove name="AspNetWindowsTokenRoleProvider"/>
27: <add applicationName="/Votpusk" name="AspNetSqlProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="LocalSqlServer"/>
28: </providers>
29: </roleManager>
30: <membership>
31: <providers>
32: <remove name="AspNetSqlMembershipProvider"/>
33: <add applicationName="/Votpusk" name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider,System.Web, Version=2.0.0.0, Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="LocalSqlServer" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="False" requiresUniqueEmail="True" minRequiredPasswordLength="1" minRequiredNonalphanumericCharacters="0" passwordFormat="Hashed" maxInvalidPasswordAttempts="6" passwordAttemptWindow="10" passwordStrengthRegularExpression=""/>
34: </providers>
35: </membership>
36: <compilation debug="true" strict="false" explicit="true">
37: </compilation>
38: <pages>
39: <namespaces>
40: <clear/>
41: <add namespace="System"/>
42: <add namespace="System.Collections"/>
43: <add namespace="System.Collections.Generic"/>
44: <add namespace="System.Collections.Specialized"/>
45: <add namespace="System.Configuration"/>
46: <add namespace="System.Text"/>
47: <add namespace="System.Text.RegularExpressions"/>
48: <add namespace="System.Linq"/>
49: <add namespace="System.Xml.Linq"/>
50: <add namespace="System.Web"/>
51: <add namespace="System.Web.Caching"/>
52: <add namespace="System.Web.SessionState"/>
53: <add namespace="System.Web.Security"/>
54: <add namespace="System.Web.Profile"/>
55: <add namespace="System.Web.UI"/>
56: <add namespace="System.Web.UI.WebControls"/>
57: <add namespace="System.Web.UI.WebControls.WebParts"/>
58: <add namespace="System.Web.UI.HtmlControls"/>
59: </namespaces>
60: </pages>
61: </system.web>
62: <system.codedom/>
63: <system.webServer/>
64: </configuration>
Теперь посмотрим как устроен более интересный компонент - синхронизатор, который необходим для всех сайтов где файлы физически хранятся в файловой системе, а их учет обеспевивается в базе. Понятно, что в силу тех или иных причин со временем учет в базе расходится с фактически хранящимися на дискке файлами. И в учете в базе появляются файлы, которых фактически нет на диске (и юзеру предлагается посмотреть видеоролик, но фактически его нет). Либо наоборот - на диске есть неучтенные видеофайлы, которые занимают место, но в учете их нет и юзер их посмотреть не может.
Этот синхронизатор выполнен мною в виде одной странички, которая позволяет рекурсивно обойти весь каталог с видеофайлами (их четыре вида, как вы видите: Upload - загружаемые пользователями файлы, Log - протоколы распознавания формата загружаемых файлов, Jpg - preview пользовательсткого ролика, Flv - сконвертированные видеоролики, пригодные к воспроизведению (на самом деле есть и пятый вид - еще один журнал виделконвертации, который вы видите на всплывающей форме неудачных конвертаций - он хранится в базе). Обнаружив все видеофайлы - синхронизатор на форме показывает - какие файлы не учтены в базе и позволяет их удалить (красные кнопки).
Итак посмотрим как устроен этот синхронизатор (пункт админки видеофайлы). С точки зрения формы - это просто табличка:
1: <%@ Page Language="VB" AutoEventWireup="false" CodeFile="AdminVideoFiles.aspx.vb" Inherits="AdminVideoFiles" %>
2:
3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4:
5: <html xmlns="http://www.w3.org/1999/xhtml">
6: <head runat="server">
7: </head>
8: <body>
9: <form id="form1" runat="server">
10: <div>
11: <asp:DataList ID="DataList1" runat="server">
12: <ItemTemplate>
13: <asp:ImageButton ID="DelButton1" ToolTip="Удалить файл" runat="server"
14: ImageUrl="~/Images/ico0013.gif" onclick="DelButton1_Click" />
15: </td><td>
16: <asp:HyperLink ID="FileLink1" runat="server" Target="_blank">HyperLink</asp:HyperLink>
17: </ItemTemplate>
18: </asp:DataList>
19: </div>
20: <asp:Label ID="Lerr1" runat="server" ForeColor="Red"></asp:Label>
21: </form>
22: </body>
23: </html>
Алгоритм у этой формы - рекурсивный обход каталога. Можно было бы сделать это на любых структурах данных, например на Дженериках , как я например сделал здесь (Практическое применение наследования, полиморфизма, интерфейсов, дженериков и делегатов на примерах в Visual Basic .NET.), но я решил эту задачку в простом классическом стиле бейсика:
1:
2: Partial Class AdminVideoFiles
3: Inherits System.Web.UI.Page
4:
5: Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
6: If Not IsPostBack Then
7: If Not Me.Page.User.Identity.IsAuthenticated Then
8: Response.Redirect("Login.aspx")
9: Exit Sub
10: End If
11: If Not Roles.IsUserInRole(Me.Page.User.Identity.Name, "UserAdmin") Then
12: Response.Redirect(Votpusk.[GoTo].NotSupported & "?ErrorMessage=""Эта функция доступна только администраторам.""&URL=""" & Request.RawUrl & """")
13: End If
14: If Request.QueryString("type") IsNot Nothing Then
15: If Request.QueryString("type") <> "" Then
16: Select Case Request.QueryString("type").ToLower
17: Case "flv"
18: CurrentAction = ActionType.FLV
19: GetFileList(HttpContext.Current, ActionType.FLV)
20: Case "log"
21: CurrentAction = ActionType.LOG
22: GetFileList(HttpContext.Current, ActionType.LOG)
23: Case "jpg"
24: CurrentAction = ActionType.JPG
25: GetFileList(HttpContext.Current, ActionType.JPG)
26: Case "upload"
27: CurrentAction = ActionType.UPLOAD
28: GetFileList(HttpContext.Current, ActionType.UPLOAD)
29: End Select
30: End If
31: End If
32: CurrentDataitemNamber = 0
33: DataList1.DataSource = FLV_DirList
34: DataList1.DataBind()
35: End If
36: End Sub
37:
38: Dim CurrentAction As ActionType, CurrentDataitemNamber As Integer = 0
39: Protected Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataListItemEventArgs) Handles DataList1.ItemDataBound
40: If e.Item.DataItem IsNot Nothing Then
41: CurrentDataitemNamber += 1
42: Dim DelButton1 As ImageButton = CType(e.Item.FindControl("DelButton1"), ImageButton)
43: Dim FileLink1 As HyperLink = CType(e.Item.FindControl("FileLink1"), HyperLink)
44:
45: Dim FullFileName As String = IO.Path.Combine(SumDirList(FLV_DirList(CurrentDataitemNamber)), FLV_FileList(CurrentDataitemNamber))
46: Dim Pos1 As Integer = FullFileName.IndexOf("FFF")
47: Dim HrefLink As String = "http://video.votpusk.ru/FFF/" & FullFileName.Substring(Pos1 + 4).Replace("\", "/")
48: FileLink1.NavigateUrl = HrefLink
49: FileLink1.Text = FullFileName
50: If Not FileExistInDB(FLV_FileList(e.Item.DataItem), CurrentAction) Then
51: DelButton1.Visible = True
52: DelButton1.CommandName = FullFileName
53: Else
54: DelButton1.Visible = False
55: End If
56: End If
57: End Sub
58:
59: Sub GetFileList(ByVal context As HttpContext, ByVal ListActionType As ActionType)
60: Dim LocalVideoDirectory As String = context.Server.MapPath("FFF")
61: FLV_FileList = New Collection
62: FLV_DirList = New Collection
63: SumDirList = New Collection
64: Select Case ListActionType
65: Case ActionType.FLV : ProcessDir(LocalVideoDirectory, "*.flv")
66: Case ActionType.LOG : ProcessDir(LocalVideoDirectory, "*.log")
67: Case ActionType.JPG : ProcessDir(LocalVideoDirectory, "*.jpg")
68: Case ActionType.UPLOAD : ProcessDir(LocalVideoDirectory, "*.upload")
69: End Select
70: End Sub
71:
72: Function FileExistInDB(ByVal FileName As String, ByVal CheckActionType As ActionType) As Boolean
73: Select Case CheckActionType
74: Case ActionType.FLV
75: Dim CheckVideoFlvFile As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, "CheckVideoFlvFile")
76: CheckVideoFlvFile.SelectCommandType = SqlDataSourceCommandType.StoredProcedure
77: CheckVideoFlvFile.SelectParameters.Add("FlvFile", FileName)
78: Dim DV1 As Data.DataView = CheckVideoFlvFile.Select(New DataSourceSelectArguments)
79: If DV1 Is Nothing Then
80: Return False
81: Else
82: If DV1.Count = 0 Then
83: Return False
84: Else
85: Return True
86: End If
87: End If
88:
89: Case ActionType.JPG
90: Dim CheckVideoJPGFile As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, "CheckVideoJpgFile")
91: CheckVideoJPGFile.SelectCommandType = SqlDataSourceCommandType.StoredProcedure
92: CheckVideoJPGFile.SelectParameters.Add("JpgFile", FileName)
93: Dim DV1 As Data.DataView = CheckVideoJPGFile.Select(New DataSourceSelectArguments)
94: If DV1 Is Nothing Then
95: Return False
96: Else
97: If DV1.Count = 0 Then
98: Return False
99: Else
100: Return True
101: End If
102: End If
103: Case ActionType.UPLOAD
104: Dim CheckVideoUploadFile As New SqlDataSource(System.Configuration.ConfigurationManager.ConnectionStrings("SQLServer_ConnectionStrings").ConnectionString, "CheckVideoSourceFile")
105: CheckVideoUploadFile.SelectCommandType = SqlDataSourceCommandType.StoredProcedure
106: CheckVideoUploadFile.SelectParameters.Add("SourceFile", FileName)
107: Dim DV1 As Data.DataView = CheckVideoUploadFile.Select(New DataSourceSelectArguments)
108: If DV1 Is Nothing Then
109: Return False
110: Else
111: If DV1.Count = 0 Then
112: Return False
113: Else
114: Return True
115: End If
116: End If
117: Case ActionType.LOG
118: 'не учитываются в базе
119: Return False
120: End Select
121:
122: End Function
123:
124: Enum ActionType
125: FLV = 1
126: LOG = 2
127: JPG = 3
128: UPLOAD = 4
129: End Enum
130:
131: Dim FLV_FileList As Collection
132: Dim FLV_DirList As Collection
133: Dim SumDirList As Collection
134:
135: ''' <summary>
136: ''' Рекурсивный обход каталогов
137: ''' </summary>
138: Private Sub ProcessDir(ByVal StartPath As String, ByVal FileExtension As String)
139: If StartPath.Contains(":\RECYCLER\") Or StartPath.Contains(":\System Volume Information") Then
140: 'корзину и системные области каждого диска не чистим
141: Exit Sub
142: End If
143: SumDirList.Add(StartPath)
144: '
145: Dim X As New IO.DirectoryInfo(StartPath)
146: Dim _FLV_FileList() As IO.FileInfo
147: Dim DirList() As IO.DirectoryInfo
148: '
149: Try
150: _FLV_FileList = X.GetFiles(FileExtension)
151: Catch ex As Exception
152: 'молча
153: Exit Sub
154: End Try
155: '
156: Try
157: For Each OneFile As IO.FileInfo In _FLV_FileList
158: FLV_DirList.Add(SumDirList.Count) 'поставим ссылочку на текущую директорию
159: FLV_FileList.Add(OneFile.Name)
160: Next
161: Catch ex As Exception
162: 'молча
163: End Try
164: '
165: Try
166: DirList = X.GetDirectories()
167: Catch ex As Exception
168: 'молча
169: Exit Sub
170: End Try
171: '
172: For Each OneDir As IO.DirectoryInfo In DirList
173: ProcessDir(OneDir.FullName, FileExtension)
174: Next
175: End Sub
176:
177:
178: Protected Sub DelButton1_Click(ByVal sender As Object, ByVal e As System.Web.UI.ImageClickEventArgs)
179: Dim DelButton1 As ImageButton = CType(sender, ImageButton)
180: Dim FullFileName As String = DelButton1.CommandName
181: If My.Computer.FileSystem.FileExists(FullFileName) Then
182: Try
183: My.Computer.FileSystem.DeleteFile(FullFileName)
184: Lerr1.Text = ""
185: Catch ex As Exception
186: Lerr1.Text = ex.Message
187: End Try
188: Else
189: Lerr1.Text = "file not found"
190: End If
191: End Sub
192: End Class
Если у вашей фирмы нет хорошего программиста для построения видеохостингов и видеоконвертации с нуля, вы можете заказать готовое решение у меня по реквизитам указанным на портале //www.vb-net.com/.
|