Я не согласен с «невозможным», обозначенным как правильный ответ. Если кто-то все еще ищет возможность, вот работа, которая работала для меня. Я использую MVC5. Идея заключается в использовании переменной сеанса. Я получил эту идею из ASP.Net Form .
My Model / ViewModel (только соответствующие свойства):
public partial class emp_leaves
{
public string fileNameOrig { get; set; }
public byte[] fileContent { get; set; }
public HttpPostedFileBase uploadFile { get; set; }
}
В моем контроллере (HttpPost) : // Check
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(emp_leaves emp_leaves)
{
if (emp_leaves.uploadFile != null && emp_leaves.uploadFile.ContentLength>0 && !string.IsNullOrEmpty(emp_leaves.uploadFile.FileName))
{
emp_leaves.fileNameOrig = Path.GetFileName(emp_leaves.uploadFile.FileName);
emp_leaves.fileContent = new byte[emp_leaves.uploadFile.ContentLength];
emp_leaves.uploadFile.InputStream.Read(emp_leaves.fileContent, 0, emp_leaves.uploadFile.ContentLength);
Session["emp_leaves.uploadFile"] = emp_leaves.uploadFile; //saving the file in session variable here
}
else if (Session["emp_leaves.uploadFile"] != null)
{//if re-submitting after a failed validation you will reach here.
emp_leaves.uploadFile = (HttpPostedFileBase)Session["emp_leaves.uploadFile"];
if (emp_leaves.uploadFile != null && emp_leaves.uploadFile.ContentLength>0 && !string.IsNullOrEmpty(emp_leaves.uploadFile.FileName))
{
emp_leaves.fileNameOrig = Path.GetFileName(emp_leaves.uploadFile.FileName);
emp_leaves.uploadFile.InputStream.Position = 0;
emp_leaves.fileContent = new byte[emp_leaves.uploadFile.ContentLength];
emp_leaves.uploadFile.InputStream.Read(emp_leaves.fileContent, 0, emp_leaves.uploadFile.ContentLength);
}
}
//code to save follows here...
}
Наконец, в моем представлении редактирования: здесь я условно показываю управление загрузкой файла.
< script type = "text/javascript" >
$("#removefile").on("click", function(e) {
if (!confirm('Delete File?')) {
e.preventDefault();
return false;
}
$('#fileNameOrig').val('');
//toggle visibility for concerned div
$('#downloadlrfdiv').hide();
$('#uploadlrfdiv').show();
return false;
}); <
/script>
@model PPMSWEB.Models.emp_leaves @{ HttpPostedFileBase uploadFileSession = Session["emp_leaves.uploadFile"] == null ? null : (HttpPostedFileBase)Session["emp_leaves.uploadFile"]; } @using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data"
})) { @Html.AntiForgeryToken()
@*irrelevant content removed*@
@Model.fileNameOrig
@if (isEditable && !Model.readonlyMode)
{
@Html.Raw(" ");
}
@Html.TextBoxFor(model => model.uploadFile, new { @type = "file", @class = "btn btn-default", @title = "Upload file (max 300 KB)" }) @Html.ValidationMessageFor(x => x.uploadFile)
}
Не зная вашего полного приложения, трудно сказать, каков наилучший способ решения проблемы, но распространенным методом является использование заголовка, который начинается с поля фиксированной длины, которое обозначает длину остальной части вашего сообщения. .
Предположим, что ваш заголовок состоит только из 4-байтового целого числа, которое обозначает длину остальной части вашего сообщения. Затем просто сделайте следующее.
// This assumes buffer is at least x bytes long,
// and that the socket is blocking.
void ReadXBytes(int socket, unsigned int x, void* buffer)
{
int bytesRead = 0;
int result;
while (bytesRead < x)
{
result = read(socket, buffer + bytesRead, x - bytesRead);
if (result < 1 )
{
// Throw your error.
}
bytesRead += result;
}
}
Затем в коде
unsigned int length = 0;
char* buffer = 0;
// we assume that sizeof(length) will return 4 here.
ReadXBytes(socketFileDescriptor, sizeof(length), (void*)(&length));
buffer = new char[length];
ReadXBytes(socketFileDescriptor, length, (void*)buffer);
// Then process the data as needed.
delete [] buffer;
делается несколько предположений:
Поскольку принято хотеть явно знать размер целого числа, которое вы посылаете по сети, определите их в заголовочном файле и используйте их явно, например:
// These typedefs will vary across different platforms
// such as linux, win32, OS/X etc, but the idea
// is that a Int8 is always 8 bits, and a UInt32 is always
// 32 bits regardless of the platform you are on.
// These vary from compiler to compiler, so you have to
// look them up in the compiler documentation.
typedef char Int8;
typedef short int Int16;
typedef int Int32;
typedef unsigned char UInt8;
typedef unsigned short int UInt16;
typedef unsigned int UInt32;
Это будет измените вышеприведенное на:
UInt32 length = 0;
char* buffer = 0;
ReadXBytes(socketFileDescriptor, sizeof(length), (void*)(&length));
buffer = new char[length];
ReadXBytes(socketFileDescriptor, length, (void*)buffer);
// process
delete [] buffer;
Надеюсь, это поможет.
Если вы фактически создаете буфер в соответствии с предложением dirks, то:
int readResult = read(socketFileDescriptor, buffer, BUFFER_SIZE);
может полностью заполнить буфер, возможно, перезаписывая символ завершающего нуля, от которого вы зависите при извлечении в поток строк. Вам нужно:
int readResult = read(socketFileDescriptor, buffer, BUFFER_SIZE - 1 );
Это статья, на которую я всегда ссылаюсь при работе с сокетами.
Он покажет вам, как надежно использовать 'select ()', и содержит некоторые другие полезные ссылки внизу для получения дополнительной информации о сокетах.
1) Другие (особенно в срочном порядке) отметили, что для буфера должно быть выделено некоторое пространство памяти. Для небольших значений N (скажем, N < = 4096) вы также можете выделить его в стеке:
#define BUFFER_SIZE 4096
char buffer[BUFFER_SIZE]
Это избавит вас от беспокойства по поводу того, что буфер delete[]
должен быть исключением быть брошенным.
Но помните, что стеки имеют конечный размер (как и кучи, но стеки более конечные), так что вы не хотите помещать туда слишком много.
2) Для кода возврата -1 вы не должны просто немедленно возвращаться (немедленное создание исключения еще более схематично.) Существуют определенные нормальные условия, с которыми вам нужно справиться, если ваш код должен быть чем-то большим, чем короткое домашнее задание. Например, EAGAIN может быть возвращен в errno, если в настоящее время нет данных на неблокирующем сокете. Взгляните на справочную страницу для чтения (2).
Где Вы выделяете память для Вашего buffer
? Строка, где Вы вызываете bzero
, вызывает неопределенное поведение, так как буфер не указывает ни на какой допустимый регион памяти.
char *buffer = new char[ BUFFER_SIZE ];
// do processing
// don't forget to release
delete[] buffer;
Несколько указателей:
необходимо обработать возвращаемое значение 0, который говорит Вам, что удаленный хост закрыл сокет.
Для неблокирования сокетов, также необходимо проверить ошибочное возвращаемое значение (-1) и удостовериться, что errno не является EINPROGRESS, который ожидается.
Вам определенно нужна лучшая обработка ошибок - Вы потенциально пропускаете буфер, на который указывает 'буфер'. Который, я заметил, Вы не выделяете нигде в этом фрагменте кода.
Кто-то еще сделал правильное замечание о том, как Ваш буфер не является завершенной струной до пустого указателя, если Ваше чтение () заполняет весь буфер. Это - действительно проблема и серьезная.
Ваш размер буфера является немного маленьким, но должен работать, пока Вы не пытаетесь считать больше чем 256 байтов, или независимо от того, что Вы выделяете для него.
, Если Вы волнуетесь по поводу вхождения в бесконечный цикл, когда удаленный хост отправляет Вам уродливое сообщение (потенциальная атака "отказ в обслуживании") тогда, необходимо использовать выбор () с тайм-аутом на сокете, чтобы проверить на удобочитаемость и только читать, если данные доступны, и прыгните с парашютом, если выбор () испытывает таймаут.
Что-то вроде этого могло бы работать на Вас:
fd_set read_set;
struct timeval timeout;
timeout.tv_sec = 60; // Time out after a minute
timeout.tv_usec = 0;
FD_ZERO(&read_set);
FD_SET(socketFileDescriptor, &read_set);
int r=select(socketFileDescriptor+1, &read_set, NULL, NULL, &timeout);
if( r<0 ) {
// Handle the error
}
if( r==0 ) {
// Timeout - handle that. You could try waiting again, close the socket...
}
if( r>0 ) {
// The socket is ready for reading - call read() on it.
}
В зависимости от объема данных Вы ожидаете получать, способ, которым Вы неоднократно сканируете все сообщение для "конца"; маркер очень неэффективен. Это лучше сделано с конечным автоматом (состояния, являющиеся 'e '->'n '->'d '->'';) так, чтобы Вы только посмотрели на каждый входящий символ однажды.
И серьезно, необходимо рассмотреть нахождение, что библиотека делает все это для Вас. Это не легкое получение его право.
Только добавить к вещам из нескольких из сообщений выше:
чтение () - по крайней мере, в моей системе - возвращает ssize_t. Это похоже на size_t, кроме подписывается. В моей системе это - длинное, не интервал. Вы могли бы получить предупреждения компилятора при использовании интервала, в зависимости от системы, компилятора, и какие предупреждения Вы включили.